Los iteradores de Rust son zero-cost abstractions: escribes map, filter y collect encadenados y el compilador genera código tan eficiente como si hubieras escrito un bucle for a mano. No hay objetos intermedios, no hay asignaciones de memoria innecesarias. Solo código máquina optimizado.
El trait Iterator
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
// ...más de 70 métodos con implementaciones por defecto
}
Solo necesitas implementar next(). Los demás métodos (map, filter, collect, fold
) vienen gratis.
iter(), iter_mut() e into_iter()
let v = vec![1, 2, 3];
// iter(): presta cada elemento (&T)
for x in v.iter() {
println!("{}", x); // x es &i32
}
// iter_mut(): presta mutablamente (&mut T)
let mut v2 = vec![1, 2, 3];
for x in v2.iter_mut() {
*x *= 2; // x es &mut i32
}
// into_iter(): consume la colección (T)
for x in v.into_iter() {
println!("{}", x); // x es i32, v ya no existe
}
Adaptadores: transforman el iterador
let v = vec![1, 2, 3, 4, 5, 6];
// map: aplica una función a cada elemento
let cuadrados: Vec<_> = v.iter().map(|&x| x * x).collect();
// filter: conserva solo los que cumplen la condición
let pares: Vec<_> = v.iter().filter(|&&x| x % 2 == 0).collect();
// flat_map: aplana el resultado
let palabras = vec!["hola mundo", "foo bar"];
let letras: Vec<&str> = palabras.iter()
.flat_map(|s| s.split_whitespace())
.collect();
// ["hola", "mundo", "foo", "bar"]
// zip: combina dos iteradores en pares
let a = vec![1, 2, 3];
let b = vec!["uno", "dos", "tres"];
let pares: Vec<_> = a.iter().zip(b.iter()).collect();
// [(1, "uno"), (2, "dos"), (3, "tres")]
Consumidores: producen un valor final
let v = vec![1, 2, 3, 4, 5];
// collect: recoge en una colección
let v2: Vec<i32> = v.iter().map(|&x| x * 2).collect();
// sum / product
let suma: i32 = v.iter().sum();
let producto: i32 = v.iter().product();
// fold: reduce con acumulador
let suma_manual = v.iter().fold(0, |acc, &x| acc + x);
// count: número de elementos
let n = v.iter().filter(|&&x| x > 2).count(); // 3
// any / all
let alguno_par = v.iter().any(|&x| x % 2 == 0); // true
let todos_positivos = v.iter().all(|&x| x > 0); // true
// find
let primero_par = v.iter().find(|&&x| x % 2 == 0); // Some(2)
// max / min
println!("{:?}", v.iter().max()); // Some(5)
Por qué son zero-cost
Los adaptadores son lazy: no hacen nada hasta que un consumidor los activa. Rust fusiona toda la cadena en un único bucle en tiempo de compilación.
// Este código genera el mismo assembly que el bucle manual
let resultado: i32 = (0..1_000_000)
.filter(|x| x % 2 == 0)
.map(|x| x * x)
.take(100)
.sum();
Depurar con inspect
let resultado: Vec<_> = vec![1, 2, 3, 4, 5]
.iter()
.inspect(|x| println!("antes filter: {}", x))
.filter(|&&x| x % 2 == 0)
.inspect(|x| println!("después filter: {}", x))
.collect();
Resumen
iter(): referencias;iter_mut(): referencias mutables;into_iter(): consume la colección.- Adaptadores:
map,filter,flat_map,zip,take,skip(lazy). - Consumidores:
collect,sum,fold,count,any,all,find(activan la cadena). - Zero-cost: el compilador fusiona la cadena en un único bucle sin overhead.
inspect: útil para depurar pipelines de iteradores.
El siguiente artículo profundiza en cómo encadenar iteradores para construir pipelines de transformación complejos y explora adaptadores avanzados como flat_map, take_while y peekable.
