diff --git a/0000-iterable.md b/0000-iterable.md new file mode 100644 index 00000000000..76ba3e5d455 --- /dev/null +++ b/0000-iterable.md @@ -0,0 +1,155 @@ +- Start Date: 2014-03-20 +- RFC PR #: (leave this empty) +- Rust Issue #: (leave this empty) + +# Summary + +Add an `Iterable` family of traits that will allow a function to consume both +an iterator or a value that can be converted into an iterator. + +# Motivation + +We have some unnecessary redundancy in some of our collection APIs: + + * `Vec::push_all` is designed to take one `Vec` and merge it into another. + * `Extendable::extend` takes an Iterator and merges it into a value. + * `vec::append` copies and merges one `Vec` with anther `Vec`. + +# Detailed design + +This redundancy can be eliminated with a simple trait: + +```rust +/// Any type that implements `Iterable` can be iterated over with the +/// iterator `I` to yield values of type `T`. Because of Rusts move semantics, +/// this means that `self` needs to be taken by value, which for moving types +/// means turning `self` into a iterator. +trait Iterable> { + fn into_iter(self) -> I; + + fn map<'r, B>(self, f: 'r |A| -> B) -> iter::Map<'r, A, B, I> { + self.into_iter().map(f) + } + + fn collect>(self) -> B { + iter::FromIterator::from_iterator(&mut self.into_iter()) + } + + // ... all old Iterator adapters that take `self` and make sense + // to provide on a data structure directly +} +``` + +This trait is implementable by both values and iterators. Values would opt-in +to implementing `Iterable`, where we would move all `.move_iter()`-style +methods to impls of `Iterable`: + +```rust +use std::vec_ng::{Vec, Items, MutItems, MoveItems}; + +impl Iterable> for Vec { + fn into_iter(self) -> MoveItems { + self.move_iter() + } +} +``` + +Iterators would now be required to implement `Iterable`: + +```rust +trait Iterator: Iterable { + ... +} + +impl Iteratable> for MoveItems { + fn into_iter(self) -> MoveItems { self } +} +``` + +Since every Iterator will have the same implementation so this could be a good +use case for a macro. + +Additionally, we would add two similar traits to handle returning references: + +```rust +trait RefIterable<'a, A, I: iter::Iterator> { + fn refs(&'a self) -> I; +} + +trait MutIterable<'a, A, I: iter::Iterator> { + fn mut_refs(&'a mut self) -> I; +} +``` + +We also could support more optimal iterators for collections of `Pod`s. This +allows users to replaces calls of `x.refs().map(|x| *x)` with `x.values()`: + +```rust +/// Automatically implemented for all `RefIterable<&A>` with `A: Pod` +trait PodIterable<'a, A: Pod, I: iter::Iterator> { + fn values(&'a self) -> Values; +} + +// Automatically implement for all `RefIterable<&A>` with `A: Pod` +impl<'a, T: Pod, RefIter: iter::Iterator<&'a T>, Iter: RefIterable<'a, &'a T, RefIter>> + PodIterable<'a, &'a T, RefIter> for Iter { + fn values(&'a self) -> Values { Values { iter: self.refs() } } +} + +///////////////////////////////////////////////////////////////////////////// + +struct Values { iter: I } +impl<'a, T: Pod, I: iter::Iterator<&'a T>> iter::Iterator for Values { + fn next(&mut self) -> Option { self.iter.next().map(|x| *x) } + fn size_hint(&self) -> (uint, Option) { self.iter.size_hint() } +} +impl<'a, T: Pod, I: iter::DoubleEndedIterator<&'a T>> iter::DoubleEndedIterator for Values { + fn next_back(&mut self) -> Option { self.iter.next_back().map(|x| *x) } +} +impl<'a, T: Pod, I: iter::ExactSize<&'a T>> iter::ExactSize for Values {} +``` + +Finally, here is a demonstration of using these traits to reimplement `Extendable`: + +```rust +trait Extendable { + fn extend, Iter: Iterable>(&mut self, x: Iter); +} + +impl Extendable for Vec { + fn extend, Iter: Iterable>(&mut self, iter: Iter) { + let mut iter = iter.into_iter(); + let (size, _) = iter.size_hint(); + self.reserve_additional(size); + + for x in &mut iter { + self.push(x); + } + } +} + +fn main() { + let mut a = Vec::new(); + a.my_extend(vec!(1, 2)); + a.my_extend(vec!(3, 4).into_iter()); + a.my_extend(vec!(5, 6).refs().map(|x| x.clone())); + a.my_extend(vec!(7, 8).values()); + println!("extend: {}", a); +} +``` + +# Alternatives + + * We just implement everything to accept iterators. It does lead to a simpler, + but less powerful, interface. + * Felix Klock (pnkfelix) suggested an alternative name of `Pushable` instead of + `Extendable`. This trait would also provide a `.push()` method for adding a + single element. + +# Unresolved questions + + * This RFC should be revisited if/when we gain associated items. It could + reduce some of the function declaration syntactic overhead. + * This RFC should be revisited if we modify the coherence rules to allow impls like + `impl Iterable for I { ... }` while still allowing for other + impls of `Iterable`.