Vec::iter în Rust

Cele trei moduri de a parcurge un vector: iter dă referințe, iter_mut dă referințe mutabile, into_iter dă elementele.

Sintaxă
pub fn iter(&self) -> Iter<'_, T>            // produce &T
pub fn iter_mut(&mut self) -> IterMut<'_, T> // produce &mut T
fn into_iter(self) -> IntoIter<T>             // produce T, consumă vectorul

Ce face Vec::iter

iter() returnează un iterator care împrumută vectorul și produce &T: poți citi elementele, iar vectorul rămâne disponibil după aceea. iter_mut() produce &mut T, deci poți modifica fiecare element pe loc (scrii *x = ...).

into_iter() (din trait-ul IntoIterator) consumă vectorul: produce elementele propriu-zise (T), mutate afară din vector. După apel, vectorul nu mai poate fi folosit. Este util când transformi elementele în altceva și nu mai ai nevoie de original.

Bucla for le apelează automat: for x in &v este v.iter(), for x in &mut v este v.iter_mut(), iar for x in v este v.into_iter(). Pe iteratorii obținuți poți înlănțui adaptoare: map, filter, enumerate, rev.

Ce returnează

Un iterator: Iter (elemente &T), IterMut (elemente &mut T) sau IntoIter (elemente T).

Exemple

Exemplul 1: Citire, modificare, consumare

fn main() {
    let mut note = vec![7, 8, 9];

    // iter(): referințe &i32, vectorul rămâne
    let suma: i32 = note.iter().sum();
    println!("suma = {suma}");

    // iter_mut(): referințe &mut i32, putem modifica
    for n in note.iter_mut() {
        *n += 1;
    }
    println!("după bonus: {:?}", note);

    // into_iter(): elementele ies din vector, vectorul este consumat
    let texte: Vec<String> = note.into_iter().map(|n| format!("nota {n}")).collect();
    println!("{:?}", texte);
}

Rezultat

suma = 24
după bonus: [8, 9, 10]
["nota 8", "nota 9", "nota 10"]

Exemplul 2: Ce face bucla for în fiecare caz

Cu &orase primim &String, cu &mut orase primim &mut String, iar cu orase primim chiar String-urile.

fn main() {
    let mut orase = vec![String::from("Iași"), String::from("Cluj")];

    for o in &orase {
        println!("citesc {o}");
    }
    for o in &mut orase {
        o.push_str(" (RO)");
    }
    for o in orase {
        let lungime = o.len();
        println!("{o} are {lungime} octeți");
    }
}

Rezultat

citesc Iași
citesc Cluj
Iași (RO) are 10 octeți
Cluj (RO) are 9 octeți

Exemplul 3: Total de plată din prețuri și cantități

zip parcurge doi vectori în paralel; iter_mut pe al treilea scrie rezultatul.

fn main() {
    let preturi = vec![5.5, 8.0, 12.25];
    let cantitati = vec![2, 3, 1];
    let mut totaluri = vec![0.0; 3];

    for ((t, p), c) in totaluri.iter_mut().zip(preturi.iter()).zip(cantitati.iter()) {
        *t = p * *c as f64;
    }
    println!("{:?}", totaluri);
    println!("de plată: {:.2} lei", totaluri.iter().sum::<f64>());
}

Rezultat

[11.0, 24.0, 12.25]
de plată: 47.25 lei

Greșeli frecvente

  • Folosești vectorul după for x in v (fără &): borrow of moved value: `v`. Bucla l-a consumat; scrie for x in &v.
  • Încerci să modifici elementul în for x in v.iter(): x este &T, deci cannot assign to `*x`, which is behind a `&` reference. Folosește iter_mut() și *x = ....
  • Uiți steluța la iter_mut: x += 1 dă eroare de tip, pentru că x este o referință. Scrie *x += 1.

Comenzi înrudite

  • Iterator: Trait-ul care descrie orice șir de valori produse pe rând: next() dă următoarea valoare, sau None la final.
  • IntoIterator: Trait-ul tipurilor care pot fi transformate într-un iterator; este ce folosește bucla for pentru a parcurge o valoare.
  • Iterator::map: Transformă fiecare valoare a iteratorului cu o funcție și dă un iterator cu rezultatele; nu calculează nimic până la consumare.
  • Iterator::enumerate: Adaugă fiecărui element poziția lui: produce perechi (index, element), cu indexul pornind de la 0.
  • Vec: Vectorul dinamic din Rust: o listă de elemente de același tip, ținute unul după altul în memorie, care crește singură.