Reputation: 617
I'm playing around with iterators and I've been using Flatten
to better understand how iterators work. This example:
assert_eq!(vec![1, 2].iter().flatten().count(), 2);
Does not compile and gives the error (well the primary error of concern):
error[E0277]: `&{integer}` is not an iterator
--> src/lib.rs:59:38
|
59 | assert_eq!(vec![1, 2].iter().flatten().count(), 2);
| ^^^^^^^ `&{integer}` is not an iterator
|
= help: the trait `std::iter::Iterator` is not implemented for `&{integer}`
= note: required because of the requirements on the impl of `std::iter::IntoIterator` for `&{integer}`
What confuses me is that it looks like Flatten
requires the nested Item
to implement IntoIterator
. I confirmed this in the rustdocs, where they declare
pub struct Flatten<I>
where
I: Iterator,
<I as Iterator>::Item: IntoIterator
Just from ready the docs, IntoIterator just provides a way to define how a type is converted into an iterator. Why couldn't the trait bound be <I as Iterator>::Item: Iterator
? Surely if the nested item implements Iterator
, then we get the same Flatten
since the nested item's must be iterators themselves. What difference/benefit does it bring to use IntoIterator
instead?
Upvotes: 1
Views: 4643
Reputation: 30087
Requiring IntoIterator
rather than Iterator
lets you flatten a collection of collections in addition to a collection of iterators.
The collection type, for example Vec
, is different from the iterator type, so if flatten
required the items to be Iterator
s you couldn't flatten a Vec
of Vec
s.
IntoIterator
represents a type that can be converted into an iterator, such as a Vec
or many other collections (or an iterator!). So this, for example, works:
fn main() {
let nested_vec: Vec<Vec<i32>> = vec![vec![1, 2], vec![3, 4]];
let flat_vec: Vec<i32> = nested_vec.into_iter().flatten().collect();
println!("{:?}", flat_vec);
}
You can try this example on the Playground. Using into_iter()
above instead of iter()
makes sure we iterate over i32
s, not references to i32
s. (Thanks Cerberus for suggesting into_iter()
instead of copied()
.)
(You can see a few more types with IntoIterator
implementations in the docs' "Implementations on Foreign Types" section.)
Upvotes: 3