Implementar el trait Iterator en Rust para tus propios tipos

Cuando implementas el trait Iterator para un tipo, solo defines un método: next(). A cambio, obtienes gratis más de setenta métodos: map, filter, collect, enumerate, zip, take, fold… Cualquier código que acepte un iterador aceptará el tuyo automáticamente.

Contador básico

struct Contador {
    valor: u32,
    max: u32,
}

impl Contador {
    fn new(max: u32) -> Self {
        Contador { valor: 0, max }
    }
}

impl Iterator for Contador {
    type Item = u32;

    fn next(&mut self) -> Option<u32> {
        if self.valor < self.max {
            self.valor += 1;
            Some(self.valor)
        } else {
            None
        }
    }
}

fn main() {
    let c = Contador::new(5);

    // Todos estos métodos vienen gratis
    let suma: u32 = c.sum();
    println!("Suma 1..5 = {}", suma); // 15

    let dobles: Vec<_> = Contador::new(5).map(|x| x * 2).collect();
    println!("{:?}", dobles); // [2, 4, 6, 8, 10]

    let pares: Vec<_> = Contador::new(10).filter(|x| x % 2 == 0).collect();
    println!("{:?}", pares); // [2, 4, 6, 8, 10]
}

Fibonacci infinito

struct Fibonacci {
    a: u64,
    b: u64,
}

impl Fibonacci {
    fn new() -> Self {
        Fibonacci { a: 0, b: 1 }
    }
}

impl Iterator for Fibonacci {
    type Item = u64;

    fn next(&mut self) -> Option<u64> {
        let siguiente = self.a + self.b;
        self.a = self.b;
        self.b = siguiente;
        Some(self.a) // infinito: nunca devuelve None
    }
}

fn main() {
    // take() limita el iterador infinito
    let fib: Vec<u64> = Fibonacci::new().take(10).collect();
    println!("{:?}", fib); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

    // Primer Fibonacci mayor que 1000
    let grande = Fibonacci::new().find(|&n| n > 1000);
    println!("{:?}", grande); // Some(1597)
}

Árbol binario con recorrido en orden

enum Arbol {
    Vacio,
    Nodo { valor: i32, izq: Box<Arbol>, der: Box<Arbol> },
}

struct RecorridoEnOrden {
    pila: Vec<i32>,
}

impl RecorridoEnOrden {
    fn new(arbol: &Arbol) -> Self {
        let mut pila = Vec::new();
        Self::recopilar(arbol, &mut pila);
        RecorridoEnOrden { pila }
    }

    fn recopilar(arbol: &Arbol, pila: &mut Vec<i32>) {
        if let Arbol::Nodo { valor, izq, der } = arbol {
            Self::recopilar(izq, pila);
            pila.push(*valor);
            Self::recopilar(der, pila);
        }
    }
}

impl Iterator for RecorridoEnOrden {
    type Item = i32;

    fn next(&mut self) -> Option<i32> {
        if self.pila.is_empty() { None } else { Some(self.pila.remove(0)) }
    }
}

IntoIterator: para usar en for loops

struct Rango {
    inicio: i32,
    fin: i32,
}

impl IntoIterator for Rango {
    type Item = i32;
    type IntoIter = std::ops::Range<i32>;

    fn into_iter(self) -> Self::IntoIter {
        self.inicio..self.fin
    }
}

fn main() {
    let r = Rango { inicio: 1, fin: 6 };
    for n in r {
        print!("{} ", n); // 1 2 3 4 5
    }
}

Resumen

  • Implementa solo next() -> Option<Item> y obtienes más de 70 métodos gratis.
  • Los iteradores infinitos (que nunca devuelven None) se limitan con take().
  • Implementa IntoIterator para que tu tipo funcione directamente en bucles for.
  • El trait Iterator es la abstracción más reutilizable del ecosistema Rust.

El siguiente artículo cubre Box<T>: cuándo necesitas asignar en el heap, cómo usar tipos recursivos y el dispatch dinámico con Box<dyn Trait>.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP