-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Implement FromIterator for the unit type #1130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
||
Implement the trait `std::iter::FromIterator<()>` for the primitive type `()`. | ||
|
||
The implementation is very short: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this should run the iterator, rather than discard it, e.g.
fn from_iter<T>(iter: T) -> () where T: IntoIterator<Item = ()> {
for _ in iter {}
}
I also wonder if it could be used as a general purpose discard:
impl<X> FromIterator<X> for () {
fn from_iter<T>(iter: T) -> () where T: IntoIterator<Item = X> {
for _ in iter {}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question. If the exhausting implementation is chosen, the generic version seems strictly better as far as I can tell. It could be used, for example, to collect any iterable of Result<T, E>
into a Result<(), E>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need another general-purpose discard for iterators.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @huonw that this probably wants to exhaust the iterator at least, and we can always leave the X
type parameter (exhausting all kinds of iterators) to a future extension.
Seeing how iterators are lazy in general, I think This also ties in with the future possibility of other fixed-sized containers: let v: FixedCapVec<X, 4> = iter.collect(); // Expected not to iterate more than 4 times.
let v: OptionLike<X> = iter.collect(); // Expected not to iterate more than 1 times.
let v: () = iter.collect(); // Expected not to iterate more than 0 times. |
What about find? fn forward_values<T>(src: Receiver<T>, dst: Sender<T>) -> Result<(), SendError<T>> {
src.iter().map(|val| dst.send(val)).find(|v|.is_err()).or(Ok(()))
} |
@Kimundi I agree with your point. A generic implementation would still be useful. |
This is better written with and yes, I'd propose moving |
I think this question right here is actually a serious problem for this proposal. There's valid arguments both ways, and I'm inclined to think that the unclear obvious answer to this means we shouldn't do it at all. As @bluss has said, you can implement this with let value = result::fold(src, (), |_,_| ()); It might be worth adding a function |
I'd personally find it pretty surprising for I'm also personally a fan of "just write the loop" which is IMO much clearer and easier to maintain than a pile of iterator adaptors. Particularly when you're running the code for the side-effects. |
Collect ought always to exhaust the iterator; collecting an iterator consumes it. No std impl of |
Consuming an iterator or not doesn't matter: consider passing by-ref iterators, where the owner of the iterator can still access it after collect. Taking iterators by value is just the most universal interface, because it allows us to pass iterators either by value or as When collecting or extending into a fixed size data structure, I've for now chosen to not exhaust the iterator, but instead take only as many elements that fits. |
I suppose I wouldn't expect a data structure with a fixed len to exhaust the remainder of the iterator, and a |
|
At first glance that seems like a reasonable argument, except fixed-length data structures in general cannot implement FromIterator at all, because there would be no valid behavior for if the iterator did not have enough elements. Only the degenerate case of a zero-length data structure can do that, which kind of defeats the point of considering () as a fixed-length data structure of length 0. |
@kballard: For fixed length data structures there is always the option of And fixed capacity structures wouldn't have the this problem at all. |
This RFC is now entering its final comment period. |
The special-case implementation for |
I'm not sure a non-lazy behavior is the right choice here. In the future we might want to implement lazy |
It seems like there's no agreement on what the behaviour of this code should be. It's not clear to me that it can be agreed on. As such I'd suggest not going forward with this RFC, to avoid constant surprise over this behaviour. |
I agree with Gankro here. If it's lazy, that seems to contradict the motivation of the RFC, if it's strict, then it's inconsistent with the general behaviour of iterators. Since both interpretations are equally intuitive, I can see this being a source of confusion going forwards. |
The consensus of the libs team is to not merge this RFC at this time, so I'm going to close this. Thanks regardless for the RFC though @fenhl! |
The exhausting implementation of this ended up getting implemented in rust-lang/rust#45379. |
Implement
FromIterator<()>
for the empty tuple type()
aka unit, allowingResult::from_iter
to be used withResult<(), E>
.Rendered