From fd0336bbaa1b1837c7d1d6282114faa17e671b66 Mon Sep 17 00:00:00 2001 From: Fenhl Date: Wed, 20 May 2015 01:32:02 +0000 Subject: [PATCH 1/3] Propose implementing FromIterator for unit --- text/0000-unit-from-iterator.md | 84 +++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 text/0000-unit-from-iterator.md diff --git a/text/0000-unit-from-iterator.md b/text/0000-unit-from-iterator.md new file mode 100644 index 00000000000..de4ebeb5c97 --- /dev/null +++ b/text/0000-unit-from-iterator.md @@ -0,0 +1,84 @@ +- 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::, MyError>::from_iter(vec![Ok(4), Ok(7), Ok(-3903)])); // Ok([4, 7, -3903]) +println!("{:?}", Result::, MyError>::from_iter(vec![Ok(5), Err(MyError::DivisionByZero), Ok(842), Err(MyError::Overflow)])); // Err(MyError::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(src: Receiver, dst: Sender) -> Result<(), SendError> { + 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(src: Receiver, dst: Sender) -> Result<(), SendError> { + 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) -> () where T: IntoIterator { + () + } +} +``` + +# 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(src: Receiver, dst: Sender) -> Result<(), SendError> { + for val in src { + try!(dst.send(val)); + } + Ok(()) + } + ``` +* Add a special-cased `FromIterator` implementation for `Result`: + + ```rust + Impl FromIterator> for Result<(), E> { + fn from_iter(iterable: T) -> Result<(), E> where T: Iterator> { + for val in iterable { + try!(val); + } + Ok(()) + } + } + ``` + +# Unresolved questions + +None at this time. From e5887bf6b69934706e2b460e897822947e2151e1 Mon Sep 17 00:00:00 2001 From: Fenhl Date: Wed, 20 May 2015 01:46:32 +0000 Subject: [PATCH 2/3] Fix minor error in code example --- text/0000-unit-from-iterator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-unit-from-iterator.md b/text/0000-unit-from-iterator.md index de4ebeb5c97..e668584596c 100644 --- a/text/0000-unit-from-iterator.md +++ b/text/0000-unit-from-iterator.md @@ -13,7 +13,7 @@ The `Result` type implements `FromIterator` in a way that creates a result out o ```rust println!("{:?}", Result::, MyError>::from_iter(vec![Ok(4), Ok(7), Ok(-3903)])); // Ok([4, 7, -3903]) -println!("{:?}", Result::, MyError>::from_iter(vec![Ok(5), Err(MyError::DivisionByZero), Ok(842), Err(MyError::Overflow)])); // Err(MyError::DivisionByZero) +println!("{:?}", Result::, 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>`. From d9926da272c722f63312d8191a86b629ecfc33b7 Mon Sep 17 00:00:00 2001 From: Fenhl Date: Wed, 20 May 2015 01:58:04 +0000 Subject: [PATCH 3/3] Add unresolved questions as per @huonw's comment --- text/0000-unit-from-iterator.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/text/0000-unit-from-iterator.md b/text/0000-unit-from-iterator.md index e668584596c..7f4d5f2072d 100644 --- a/text/0000-unit-from-iterator.md +++ b/text/0000-unit-from-iterator.md @@ -81,4 +81,26 @@ The only known drawback is that the use-cases for this functionality seem to be # Unresolved questions -None at this time. +* Should the iterator be exhausted rather than discarded? An exhausting implementation: + + ```rust + impl FromIterator<()> for () { + fn from_iter(iter: T) -> () where T: IntoIterator { + 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 FromIterator for () { + fn from_iter(iter: T) -> () where T: IntoIterator { + for _ in iter {} + } + } + ``` + + This would allow `.collect::<()>()` to be used as a general-purpose “exhaust and discard the entire iterable” function.