IntoIterator în Rust

Trait-ul tipurilor care pot fi transformate într-un iterator; este ce folosește bucla for pentru a parcurge o valoare.

Sintaxă
pub trait IntoIterator {
    type Item;
    type IntoIter: Iterator<Item = Self::Item>;
    fn into_iter(self) -> Self::IntoIter;
}

Ce face IntoIterator

for x in valoare apelează de fapt IntoIterator::into_iter(valoare) și apoi next() pe rezultat. Orice tip care implementează IntoIterator poate sta după in: vectori, tablouri, intervale (0..10), HashMap, Option, referințe la colecții, iteratori (orice iterator este și IntoIterator).

Colecțiile au de obicei trei implementări: pentru Vec<T> (produce T, consumă vectorul), pentru &Vec<T> (produce &T) și pentru &mut Vec<T> (produce &mut T). De aceea for x in &v nu consumă vectorul, iar for x in v îl consumă. Din ediția 2021, tablourile se parcurg prin valoare: for x in [1, 2, 3] dă i32.

În funcții, parametrul impl IntoIterator<Item = T> acceptă orice sursă de valori: un vector, un tablou, un interval sau un lanț de adaptoare.

Ce returnează

into_iter() returnează iteratorul asociat (Self::IntoIter).

Exemple

Exemplul 1: Ce poate sta după for … in

Un Option se comportă ca o colecție cu 0 sau 1 elemente. Pentru el, compilatorul preferă totuși if let în loc de for (avertismentul for loop over an `Option`), așa că aici îl colectăm.

use std::collections::BTreeMap;

fn main() {
    for x in [1, 2, 3] {
        print!("{x} ");
    }
    println!("(tablou)");

    for c in 'a'..='e' {
        print!("{c}");
    }
    println!(" (interval de caractere)");

    let poate: Option<&str> = Some("Ana");
    let lista: Vec<&str> = poate.into_iter().collect();
    println!("{:?} (Option cu 0 sau 1 elemente)", lista);

    let m = BTreeMap::from([("Iași", 271), ("Cluj", 286)]);
    for (oras, mii) in &m {
        println!("{oras}: {mii} mii (dicționar)");
    }
}

Rezultat

1 2 3 (tablou)
abcde (interval de caractere)
["Ana"] (Option cu 0 sau 1 elemente)
Cluj: 286 mii (dicționar)
Iași: 271 mii (dicționar)

Exemplul 2: O funcție care acceptă orice sursă de numere

fn total(valori: impl IntoIterator<Item = u32>) -> u32 {
    let mut s = 0;
    for v in valori {
        s += v;
    }
    s
}

fn main() {
    println!("{}", total(vec![10, 20, 30]));
    println!("{}", total([1, 2, 3]));
    println!("{}", total(1..=100));
    println!("{}", total(Some(7)));
    let note = vec![9, 10];
    println!("{}", total(note.iter().copied()));
}

Rezultat

60
6
5050
7
19

Exemplul 3: IntoIterator pentru un tip propriu

Implementăm trait-ul pentru Clasa (consumă clasa, dă String) și pentru &Clasa (dă &String), exact ca la Vec.

struct Clasa {
    elevi: Vec<String>,
}

impl IntoIterator for Clasa {
    type Item = String;
    type IntoIter = std::vec::IntoIter<String>;
    fn into_iter(self) -> Self::IntoIter {
        self.elevi.into_iter()
    }
}

impl<'a> IntoIterator for &'a Clasa {
    type Item = &'a String;
    type IntoIter = std::slice::Iter<'a, String>;
    fn into_iter(self) -> Self::IntoIter {
        self.elevi.iter()
    }
}

fn main() {
    let clasa = Clasa { elevi: vec![String::from("Ana"), String::from("Mihai")] };
    for e in &clasa {
        println!("prezent: {e}");
    }
    for e in clasa {
        println!("absolvent: {e}");
    }
}

Rezultat

prezent: Ana
prezent: Mihai
absolvent: Ana
absolvent: Mihai

Greșeli frecvente

  • Scrii for x in v și apoi folosești v: borrow of moved value. into_iter pe Vec consumă vectorul; scrie for x in &v.
  • Încerci for c in text cu un String: `String` is not an iterator. Alege ce parcurgi: text.chars(), text.bytes() sau text.lines().

Comenzi înrudite

  • Iterator: Trait-ul care descrie orice șir de valori produse pe rând: next() dă următoarea valoare, sau None la final.
  • Vec::iter: Cele trei moduri de a parcurge un vector: iter dă referințe, iter_mut dă referințe mutabile, into_iter dă elementele.
  • FromIterator: Trait-ul tipurilor care pot fi construite dintr-un iterator; collect() îl folosește pentru a crea colecția cerută.
  • Range: Intervalul a..b: toate valorile de la a inclusiv până la b exclusiv. Este iterator, se folosește în for și la tăierea slice-urilor.
  • HashMap::iter: Parcurge perechile din dicționar: iter dă (&cheie, &valoare), iter_mut dă (&cheie, &mut valoare). Ordinea e aleatorie.