DoubleEndedIterator în Rust

Iterator care poate produce valori și de la coadă (next_back); permite rev(), rfind() și parcurgerea din ambele capete.

Sintaxă
pub trait DoubleEndedIterator: Iterator {
    fn next_back(&mut self) -> Option<Self::Item>;
    // plus: rfind, rfold, nth_back, rev (pe Iterator)...
}

Ce face DoubleEndedIterator

Un DoubleEndedIterator are, pe lângă next(), metoda next_back(), care dă următoarea valoare de la capătul celălalt. Cele două capete se apropie unul de altul și se întâlnesc: fiecare element este produs o singură dată.

Îl implementează iteratorii vectorilor, slice-urilor, intervalelor (0..10), ai lui chars(), BTreeMap și adaptoarele construite peste ei (map, filter, zip pe lungimi egale etc.). Datorită lui funcționează rev(), rfind(), rposition(), nth_back() și last() eficient.

Iteratorii care nu știu unde se termină (iter::from_fn, successors, (1..), citirea liniilor din fișier) nu îl implementează, deci rev() nu compilează pentru ei.

Ce returnează

next_back() returnează Option<Self::Item>.

Exemple

Exemplul 1: Din ambele capete

fn main() {
    let mut it = vec!["Ana", "Bogdan", "Cristina", "Dan", "Elena"].into_iter();
    println!("{:?}", it.next());
    println!("{:?}", it.next_back());
    println!("{:?}", it.next());
    println!("{:?}", it.next_back());
    println!("rămas: {:?}", it.collect::<Vec<_>>());
}

Rezultat

Some("Ana")
Some("Elena")
Some("Bogdan")
Some("Dan")
rămas: ["Cristina"]

Exemplul 2: rfind, nth_back și rposition

fn main() {
    let temperaturi = [3, 7, 12, 5, 14, 9, 2];
    let ultima_peste_10 = temperaturi.iter().rfind(|&&t| t > 10);
    let penultima = temperaturi.iter().nth_back(1);
    let poz = temperaturi.iter().rposition(|&t| t < 5);
    println!("ultima peste 10: {:?}", ultima_peste_10);
    println!("penultima: {:?}", penultima);
    println!("ultima sub 5 e pe poziția {:?}", poz);

    let litere: String = "Iași".chars().rev().collect();
    println!("{litere}");
}

Rezultat

ultima peste 10: Some(14)
penultima: Some(9)
ultima sub 5 e pe poziția Some(6)
ișaI

Exemplul 3: next_back pentru un iterator propriu

Un interval de pagini care poate fi parcurs din ambele capete. Cu next_back implementat, primim gratuit rev().

struct Pagini {
    prima: u32,
    ultima: u32,
}

impl Iterator for Pagini {
    type Item = u32;
    fn next(&mut self) -> Option<u32> {
        if self.prima > self.ultima {
            return None;
        }
        self.prima += 1;
        Some(self.prima - 1)
    }
}

impl DoubleEndedIterator for Pagini {
    fn next_back(&mut self) -> Option<u32> {
        if self.prima > self.ultima {
            return None;
        }
        self.ultima -= 1;
        Some(self.ultima + 1)
    }
}

fn main() {
    let inainte: Vec<u32> = Pagini { prima: 1, ultima: 5 }.collect();
    let inapoi: Vec<u32> = Pagini { prima: 1, ultima: 5 }.rev().collect();
    println!("{:?} / {:?}", inainte, inapoi);
}

Rezultat

[1, 2, 3, 4, 5] / [5, 4, 3, 2, 1]

Greșeli frecvente

  • Apelezi rev() pe un iterator care nu are capăt cunoscut, de exemplu (1..).rev() sau iter::from_fn(..).rev(): the trait bound `..: DoubleEndedIterator` is not satisfied. Colectează întâi într-un vector.
  • Crezi că next_back după epuizarea lui next mai dă ceva. Cele două capete împart aceleași elemente; când s-au întâlnit, ambele dau None.

Comenzi înrudite

  • Iterator::rev: Inversează direcția unui iterator: produce elementele de la ultimul la primul, fără să copieze nimic.
  • Iterator: Trait-ul care descrie orice șir de valori produse pe rând: next() dă următoarea valoare, sau None la final.
  • Iterator::nth: nth(n) returnează elementul de pe poziția n (de la 0) și îl consumă împreună cu cele dinainte; last returnează ultimul element.
  • Iterator::position: Returnează poziția (indexul de la 0) primului element care îndeplinește condiția, sau None.
  • Vec::reverse: Inversează pe loc ordinea elementelor: primul devine ultimul, ultimul devine primul.