Iterator vs IntoIterator dla spłaszczenia

Oct 06 2020

Bawię się iteratorami i używam, Flattenaby lepiej zrozumieć, jak działają iteratory. Ten przykład:

assert_eq!(vec![1, 2].iter().flatten().count(), 2);

Nie kompiluje się i podaje błąd (cóż, podstawowy błąd):

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

Co mnie wprawia w zakłopotanie, to to, że wygląda na to, że zaimplementowanie Flattenwymaga zagnieżdżenia . Potwierdziłem to w rustdocs, gdzie deklarująItemIntoIterator

pub struct Flatten<I> 
where
    I: Iterator,
    <I as Iterator>::Item: IntoIterator

Już z gotowych dokumentów, IntoIterator zapewnia sposób zdefiniowania, w jaki sposób typ jest konwertowany na iterator. Dlaczego nie mogła być związana cecha <I as Iterator>::Item: Iterator? Z pewnością, jeśli element zagnieżdżony jest implementowany Iterator, otrzymujemy to samo, Flattenponieważ element zagnieżdżony musi być sam iteratorem. Jaką różnicę / korzyść przynosi IntoIteratorzamiast tego?

Odpowiedzi

1 twotwotwo Oct 06 2020 at 11:18

Wymaganie, IntoIteratora nie Iteratorpozwala na spłaszczenie kolekcji kolekcji oprócz kolekcji iteratorów.

Na przykład typ kolekcji Vecróżni się od typu iteratora, więc jeśli flattenwymagano, aby elementy były Iterators, nie można spłaszczyć a Vecz Vecs.

IntoIteratorreprezentuje typ, który można przekształcić w iterator, taki jak Veclub wiele innych kolekcji (lub iterator!). Więc to na przykład działa:

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);
}

Możesz wypróbować ten przykład na placu zabaw . Użycie into_iter()powyższego zamiast iter()upewnia się, że iterujemy po i32s, a nie odwołaniach do i32s. (Dzięki Cerberusowi za sugestię into_iter()zamiast copied().)

(Możesz zobaczyć kilka innych typów z IntoIteratorimplementacjami w sekcji „Implementacje na typach obcych” w dokumentach ).