Kiwi breeder
Kiwi breeder

Reputation: 617

Iterator vs IntoIterator for flatten

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

Answers (1)

twotwotwo
twotwotwo

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 Iterators you couldn't flatten a Vec of Vecs.

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 i32s, not references to i32s. (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

Related Questions