Iterator vs IntoIterator per appiattire

Oct 06 2020

Sto giocando con gli iteratori e li sto usando Flattenper capire meglio come funzionano gli iteratori. Questo esempio:

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

Non si compila e dà l'errore (beh, l'errore principale di preoccupazione):

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

Ciò che mi confonde è che sembra che Flattenrichieda l'annidamento Itemper l'implementazione IntoIterator. L'ho confermato nei rustdocs, dove dichiarano

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

Appena pronti i documenti, IntoIterator fornisce solo un modo per definire come un tipo viene convertito in un iteratore. Perché il tratto non poteva essere vincolato <I as Iterator>::Item: Iterator? Sicuramente se l'elemento nidificato viene implementato Iterator, allora otteniamo lo stesso Flattenpoiché gli elementi nidificati devono essere essi stessi iteratori. Quale differenza / vantaggio apporta IntoIteratorinvece all'uso ?

Risposte

1 twotwotwo Oct 06 2020 at 11:18

Richiedere IntoIteratoranziché Iteratorconsente di appiattire una raccolta di raccolte oltre a una raccolta di iteratori.

Il tipo di raccolta, ad esempio Vec, è diverso dal tipo di iteratore, quindi se è flattennecessario che gli elementi siano Iterators non è possibile appiattire un Vecdi Vecs.

IntoIteratorrappresenta un tipo che può essere convertito in un iteratore, come una Veco molte altre raccolte (o un iteratore!). Quindi questo, ad esempio, funziona:

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

Puoi provare questo esempio su Playground . L'uso di into_iter()above invece di iter()ci assicura di iterare su i32s, non sui riferimenti a i32s. (Grazie Cerberus per il suggerimento into_iter()invece di copied().)

(Puoi vedere alcuni altri tipi con IntoIteratorimplementazioni nella sezione "Implementazioni su tipi esterni" dei documenti .)