El trait Iterator es el corazón de la iteración funcional en Rust. Su función es definir una secuencia de elementos que pueden ser recorridos uno a uno. La esencia de este trait reside en un único método requerido: next(&mut self) -> Option<Self::Item>. Este método debe devolver Some(valor) mientras existan elementos y None cuando la secuencia haya terminado. Implementar este trait permite que un tipo personalizado pueda ser utilizado en bucles for (gracias al trait IntoIterator) y sea compatible con toda la biblioteca de iteradores.
Una característica fundamental es la evaluación perezosa (lazy evaluation). Los métodos como map, filter o take son “adaptadores”. Un adaptador no transforma los datos inmediatamente; lo que hace es envolver el iterador original en una nueva estructura que contiene la lógica de transformación. El trabajo real no ocurre hasta que se invoca un consumidor (como collect, sum, o un bucle for), que es el encargado de llamar repetidamente a next(). Esta arquitectura permite a Rust optimizar el código mediante el “loop fusion” de LLVM, donde múltiples transformaciones se colapsan en un único bucle de alto rendimiento, evitando asignaciones de memoria intermedias innecesarias.
Si se implementa next() incorrectamente, se pueden producir errores críticos: si nunca devuelve None, se creará un bucle infinito; si no actualiza el estado interno, se repetirá el mismo valor indefinidamente. Además, abusar de collect() en medio de una cadena de adaptadores rompe la lazitud, forzando la creación de colecciones temporales en memoria y degradando el rendimiento.
// Implementación de un iterador personalizado
struct Contador {
actual: u32,
limite: u32,
}
impl Iterator for Contador {
// El tipo de elemento que devuelve el iterador
type Item = u32;
// La lógica de avance de la secuencia
fn next(&mut self) -> Option<Self::Item> {
if self.actual < self.limite {
self.actual += 1;
Some(self.actual)
} else {
None
}
}
}
fn main() {
// 1. Uso de iteradores sobre colecciones existentes
let numeros = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 2. Cadena de adaptadores lazy (no se ejecuta nada aún)
let transformacion = numeros
.into_iter() // Convierte el vec en un iterador (ownership)
.filter(|x| x % 2 == 0) // Adaptador: solo elementos pares
.map(|x| x * 10) // Adaptador: multiplica por 10
.take(3); // Adaptador: limita a los primeros 3 resultados
// 3. El consumidor (collect) dispara la ejecución
let resultado: Vec<u32> = transformacion.collect();
println!("Resultado de la cadena: {:?}", resultado);
// 4. Uso del iterador personalizado
let mi_contador = Contador { actual: 0, limite: 5 };
// El bucle for actúa como consumidor
println!("Iterando con Contador personalizado:");
for num in mi_contador {
println!("{}", num);
}
}
Explicación del Código
Contador: Es una estructura personalizada que mantiene el estado de la iteración mediante los camposactual(valor presente) ylimite(techo de la secuencia).impl Iterator for Contador: Aquí definimos el comportamiento de nuestra secuencia.type Item = u32: Define que el tipo de datos que el iterador entrega es un entero de 32 bits.next: Implementa la lógica de control. Incrementaself.actualy devuelveSome(self.actual)mientras sea menor quelimite. Cuandoactualalcanzalimite, devuelveNone, señalando el fin de la iteración.
numeros.into_iter(): Convierte el vector en un iterador que consume la colección, moviendo la propiedad de los elementos..filter(|x| x % 2 == 0): Un adaptador que toma una función de cierre (closure) y solo permite quenext()devuelva un valor si cumple la condición..map(|x| x * 10): Otro adaptador que transforma cada elemento obtenido mediante la función de cierre..take(3): Un adaptador que limita la secuencia. Una vez que se han producido 3 elementos, este adaptador hará que el iterador devuelvaNone, sin importar si hay más elementos en la fuente..collect(): El consumidor. Es el motor que impulsa la cadena; sin este método (o un buclefor), la variabletransformacionno habría procesado ningún número.for num in mi_contador: El bucleforutiliza internamenteinto_iter()sobremi_contador, llamando anext()de forma iterativa hasta recibirNone.
N° 65