Iterator::fold în Rust

Combină toate elementele într-o singură valoare, pornind de la o valoare inițială și aplicând o funcție pas cu pas.

Sintaxă
fn fold<B, F>(self, init: B, f: F) -> B
where F: FnMut(B, Self::Item) -> B

Ce face Iterator::fold

fold(init, |acc, x| ...) începe cu acumulatorul acc = init, apoi, pentru fiecare element, calculează noul acumulator din cel vechi și din element. Rezultatul final este ultimul acumulator. Pentru un iterator gol, rezultatul este chiar init.

sum, product, count, max sunt cazuri particulare de fold. Folosește fold când rezultatul are alt tip decât elementele (un text, un tuplu cu mai multe totaluri, o structură) sau când nu există o metodă gata făcută. Este un consumator: parcurge iteratorul (sau doar cât are nevoie) și pornește lanțul de adaptoare leneșe din fața lui.

Parametri

Parametru Descriere
init Valoarea inițială a acumulatorului (și rezultatul pentru un iterator gol).
f Closure care primește acumulatorul și elementul curent și returnează noul acumulator.

Ce returnează

Acumulatorul final, de tipul B.

Exemple

Exemplul 1: Suma, produsul și un text construit

fn main() {
    let numere = [3, 5, 2, 4];
    let suma = numere.iter().fold(0, |acc, x| acc + x);
    let produs = numere.iter().fold(1, |acc, x| acc * x);
    println!("suma = {suma}, produs = {produs}");

    let text = numere.iter().fold(String::new(), |mut acc, x| {
        if !acc.is_empty() {
            acc.push_str(" + ");
        }
        acc.push_str(&x.to_string());
        acc
    });
    println!("{text} = {suma}");
}

Rezultat

suma = 14, produs = 120
3 + 5 + 2 + 4 = 14

Exemplul 2: Minim, maxim și medie dintr-o singură trecere

Acumulatorul este un tuplu (minim, maxim, sumă). Funcționează și pentru f64, unde min()/max() de pe iterator nu există.

fn main() {
    let temperaturi = [12.5, -3.0, 7.25, 18.0, 4.5];
    let (min, max, suma) = temperaturi
        .iter()
        .fold((f64::INFINITY, f64::NEG_INFINITY, 0.0), |(mn, mx, s), &t| (mn.min(t), mx.max(t), s + t));
    println!("minim {min}, maxim {max}, medie {:.2}", suma / temperaturi.len() as f64);
}

Rezultat

minim -3, maxim 18, medie 7.85

Exemplul 3: Iterator gol: rezultatul este valoarea inițială

fn main() {
    let goale: Vec<u32> = Vec::new();
    let total = goale.iter().fold(100, |acc, x| acc + x);
    println!("{total}");
}

Rezultat

100

Greșeli frecvente

  • Folosești fold pentru o sumă simplă: sum() e mai clar.
  • Valoarea inițială are alt tip decât vrei: fold(0, |acc, x| acc + x) pe f64 dă eroare (0 e întreg). Scrie fold(0.0, ...).
  • Construiești un String cu acc + &x.to_string() la fiecare pas și apoi te miri că e lent: fiecare + poate realoca. E în regulă pentru liste mici; pentru multe elemente, collect::<Vec<_>>().join(..).

Comenzi înrudite

  • Iterator::reduce: Combină elementele două câte două, folosind primul element ca valoare inițială; returnează None pentru un iterator gol.
  • Iterator::try_fold: Ca fold, dar funcția returnează Option sau Result și oprirea are loc la primul None sau Err.
  • Iterator::sum: sum adună toate elementele, product le înmulțește; tipul rezultatului trebuie precizat.
  • Iterator::scan: Ca fold, dar produce câte un rezultat după fiecare element: o stare actualizată pas cu pas, de exemplu un total cumulat.