En Rust, toda la biblioteca estándar de colecciones se basa en el concepto de iteradores. Un iterador es un objeto que implementa el trait Iterator, en el que se define el método next(). Este método devuelve el siguiente elemento de la secuencia de datos (Option<T>, donde Some(T) es el siguiente valor, y None es el final de la secuencia).
Ejemplo de un iterador estándar:
let v = vec![1, 2, 3]; let mut iter = v.iter(); while let Some(x) = iter.next() { println!("{}", x); }
Los adaptadores de iteradores son métodos (por ejemplo, .map(), .filter(), .enumerate(), .take()) que devuelven nuevos iteradores, procesando valores "sobre la marcha" según las funciones proporcionadas.
Los iteradores personalizados se implementan creando una estructura y realizando para ella el trait Iterator con un comportamiento propio:
struct Counter { count: u8 } impl Iterator for Counter { type Item = u8; fn next(&mut self) -> Option<Self::Item> { if self.count < 5 { self.count += 1; Some(self.count) } else { None } } }
Utilice adaptadores estándar cuando son suficientes para procesar colecciones. La implementación de un iterador propio es necesaria si:
¿Se pueden crear iteradores infinitos? ¿Qué pasará si intentas reunirlos en una colección, por ejemplo, usando
.collect()?
Respuesta: Sí, en Rust se han creado iteradores como std::iter::repeat, que devuelven una secuencia infinita:
let mut endless = std::iter::repeat(1); endless.next(); // devolverá Some(1) infinitamente
Si intentas reunir tal iterador en una colección a través de .collect(), el programa se bloqueará o fallará con un desbordamiento de memoria, ¡porque la iteración no terminará por sí misma!
Historia
En un proyecto de REST API, se ordenó un array de 1000 elementos y luego se utilizó .iter().filter(|x| *x > 500), pero en lugar de .collect::<Vec<_>>(), se aplicó .fold(0, |acc, _| acc + 1) dentro de un adaptador complejo, perdiendo la comprensión de si la iteración terminaría correctamente. Resultado: bloqueos aleatorios debido a que la filtración se producía por la pereza ilimitada del iterador con un error interno.
Historia
En un motor propietario para generar id únicos, un desarrollador al azar decidió implementar su propio iterador, olvidando devolver None al alcanzar el límite. Como resultado, la iteración entró en un ciclo infinito, el servidor en producción consumió toda la CPU y no respondió a las solicitudes.
Historia
En la parte de frontend (módulo de WebAssembly), se utilizó un iterador con adaptadores anidados: .map().filter().skip(), y se intentó obtener el resultado a través de .collect() para un formulario. Al cambiar el tipo bajo el adaptador, Rust emitió un error complicado de tiempo de compilación sobre la incompatibilidad de tipos, debido a que se olvidó especificar el tipo exacto de la colección: .collect::<Vec<_>>(). El problema se resolvió añadiendo la anotación, pero se gastaron varias horas buscando la causa.