Skip to content

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
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions text/0000-unit-from-iterator.md
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:
Copy link
Member

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 {}
     }
}

Copy link
Author

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>.

Copy link
Contributor

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.

Copy link
Member

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.


```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.