-
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
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
- Feature Name: unit_from_iterator | ||
- Start Date: 2015-05-20 | ||
- RFC PR: (leave this empty) | ||
- Rust Issue: (leave this empty) | ||
|
||
# Summary | ||
|
||
Implement `std::iter::FromIterator<()>` for the empty tuple type `()`, also known as “unit”. | ||
|
||
# Motivation | ||
|
||
The `Result` type implements `FromIterator` in a way that creates a result out of an iterable of results, extracting the first error if any, and otherwise returning `Ok` with the contents of all values: | ||
|
||
```rust | ||
println!("{:?}", Result::<Vec<i32>, MyError>::from_iter(vec![Ok(4), Ok(7), Ok(-3903)])); // Ok([4, 7, -3903]) | ||
println!("{:?}", Result::<Vec<i32>, MyError>::from_iter(vec![Ok(5), Err(MyError::DivisionByZero), Ok(842), Err(MyError::Overflow)])); // Err(DivisionByZero) | ||
``` | ||
|
||
Implementing this RFC would allow this pattern to be used with iterables whose item type is of the form `Result<(), T>`. | ||
|
||
For example, suppose we have a function which moves all values from a `mpsc::Receiver` into a `mpsc::Sender`. Currently, this could be written as follows: | ||
|
||
```rust | ||
fn forward_values<T>(src: Receiver<T>, dst: Sender<T>) -> Result<(), SendError<T>> { | ||
src.iter().map(|val| dst.send(val)).fold(Ok(()), Result::and)) | ||
} | ||
``` | ||
|
||
This has the flaw of exhausting the receiver even after an error is encountered. With the proposed trait implementation, it could be refactored into the following: | ||
|
||
```rust | ||
fn forward_values<T>(src: Receiver<T>, dst: Sender<T>) -> Result<(), SendError<T>> { | ||
src.iter().map(|val| dst.send(val)).collect() | ||
} | ||
``` | ||
|
||
This version of the function immediately returns when the first error is encountered. | ||
|
||
# Detailed design | ||
|
||
Implement the trait `std::iter::FromIterator<()>` for the primitive type `()`. | ||
|
||
The implementation is very short: | ||
|
||
```rust | ||
impl FromIterator<()> for () { | ||
fn from_iter<T>(_: T) -> () where T: IntoIterator<Item = ()> { | ||
() | ||
} | ||
} | ||
``` | ||
|
||
# Drawbacks | ||
|
||
The only known drawback is that the use-cases for this functionality seem to be quite limited and as such may not warrant an addition to the standard library. However, the `()` type has only one possible value, so if more use-cases requiring an implementation of this trait for `()` are found, the proposed implementation must already be correct. | ||
|
||
# Alternatives | ||
|
||
* Do nothing. The same short-circuiting behavior shown in the example can be achieved by using a for loop and `try!`: | ||
|
||
```rust | ||
fn forward_values<T>(src: Receiver<T>, dst: Sender<T>) -> Result<(), SendError<T>> { | ||
for val in src { | ||
try!(dst.send(val)); | ||
} | ||
Ok(()) | ||
} | ||
``` | ||
* Add a special-cased `FromIterator` implementation for `Result`: | ||
|
||
```rust | ||
Impl<E> FromIterator<Result<(), E>> for Result<(), E> { | ||
fn from_iter<T>(iterable: T) -> Result<(), E> where T: Iterator<Item = Result<(), E>> { | ||
for val in iterable { | ||
try!(val); | ||
} | ||
Ok(()) | ||
} | ||
} | ||
``` | ||
|
||
# Unresolved questions | ||
|
||
* Should the iterator be exhausted rather than discarded? An exhausting implementation: | ||
|
||
```rust | ||
impl FromIterator<()> for () { | ||
fn from_iter<T>(iter: T) -> () where T: IntoIterator<Item = ()> { | ||
for _ in iter {} | ||
} | ||
} | ||
``` | ||
|
||
This would decrease performance in the `Result` example, but open up other possibilities for using this where for loops are used today. | ||
|
||
* If so, should the implementation be expanded to exhaust iterables of any item type? | ||
|
||
```rust | ||
impl<X> FromIterator<X> for () { | ||
fn from_iter<T>(iter: T) -> () where T: IntoIterator<Item = X> { | ||
for _ in iter {} | ||
} | ||
} | ||
``` | ||
|
||
This would allow `.collect::<()>()` to be used as a general-purpose “exhaust and discard the entire iterable” function. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
I also wonder if it could be used as a general purpose discard:
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 aResult<(), 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.