En Rust, los iteradores son “perezosos” (lazy); definen una secuencia de operaciones pero no ejecutan nada hasta que se consume el iterador. Los métodos consumidores son aquellos que fuerzan la evaluación de la cadena de iteración, agotando el iterador y transformando sus elementos en un valor final o una nueva estructura.
Existen diferentes categorías de consumidores según su propósito:
1. Materialización: collect es el más versátil, permitiendo transformar un iterador en colecciones como Vec<T>, HashMap<K, V> o String. Es crucial que el compilador pueda inferir el tipo de la colección mediante anotaciones de tipo (ej. collect::<Vec<_>>()) o mediante el tipo de la variable de destino, de lo contrario, el código no compilará por ambigüedad.
2. Acumulación: fold es el consumidor más potente, permitiendo reducir toda una secuencia en un único valor inicial (acc) mediante una clausura. A diferencia de fold, reduce intenta usar el primer elemento del iterador como valor inicial, devolviendo un Option en caso de que el iterador esté vacío.
3. Búsqueda y Predicados: Métodos como any y all implementan short-circuiting: any se detiene en cuanto encuentra un elemento que cumple la condición, y all se detiene si encuentra uno que no la cumple. find y position devuelven Option, reconociendo que el elemento buscado podría no existir.
4. Transformación Estructural: partition divide un iterador en dos colecciones distintas basándose en un predicado, mientras que unzip separa un iterador de tuplas (como (A, B)) en dos colecciones independientes.
Un error común es el uso excesivo de collect en medio de una cadena de iteradores. Esto provoca una asignación de memoria innecesaria; si solo quieres sumar valores, es preferible usar sum() directamente sobre el iterador en lugar de recolectar en un Vec para luego iterar sobre él.
fn main() {
let numeros = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 1. collect: Transforma el iterador en un Vec de números pares
let solo_pares: Vec<i32> = numeros.iter()
.filter(|&&x| x % 2 == 0)
.cloned()
.collect();
// 2. sum y fold: Suma simple vs acumulación personalizada (suma de cuadrados)
let suma_total: i32 = numeros.iter().sum();
let suma_cuadrados = numeros.iter().fold(0, |acc, &x| acc + x * x);
// 3. any, all y find: Evaluación lógica y búsqueda
let hay_cinco = numeros.iter().any(|&x| x == 5); // short-circuit: se detiene en 5
let todos_positivos = numeros.iter().all(|&x| x > 0);
let primer_mayor_a_7 = numeros.iter().find(|&&x| x > 7); // Devuelve Option<&i32>
// 4. partition: Separa pares e impares en dos vectores distintos
let (pares, impares): (Vec<i32>, Vec<i32>) = numeros.into_iter()
.partition(|x| x % 2 == 0);
// 5. unzip: Descompone un iterador de tuplas en dos colecciones
let pares_impares_tuplas = vec![(1, 2), (3, 4), (5, 6)];
let (lista_a, lista_b): (Vec<i32>, Vec<i32>) = pares_impares_tuplas.into_iter().unzip();
// 6. max y min: Encuentra los valores extremos
let max_valor = numeros.iter().max();
let min_valor = numeros.iter().min();
// Verificación de resultados
println!("Pares: {:?}, Impares: {:?}", solo_pares, pares);
println!("Suma: {}, Cuadrados: {}", suma_total, suma_cuadrados);
println!("¿Hay un 5?: {}, Primer > 7: {:?}", hay_cinco, primer_mayor_a_7);
println!("Lista A: {:?}, Lista B: {:?}", lista_a, lista_b);
println!("Max: {:?}, Min: {:?}", max_valor, min_valor);
}
Explicación del Código
* solo_pares: Se utiliza collect::<Vec<i32>>() (inferido por el tipo de la variable) para materializar los elementos filtrados por el predicado x % 2 == 0 en un nuevo vector.
* suma_total: El método sum() consume el iterador para calcular el total aritmético de numeros.
* suma_cuadrados: fold toma 0 como valor inicial y aplica la función de acumulación acc + x * x para cada elemento.
* hay_cinco y todos_positivos: any y all consumen el iterador de forma eficiente mediante evaluación perezosa (short-circuiting).
* primer_mayor_a_7: find busca el primer elemento que satisfaga la condición, devolviendo un Option<&i32>.
* (pares, impares): partition consume el iterador original into_iter() y separa los elementos en dos vectores según la condición de paridad.
* (lista_a, lista_b): unzip toma un iterador de tuplas y los descompone en dos colecciones separadas de tipo Vec<i32>.
* max_valor y min_valor: max() y min() devuelven un Option debido a que el iterador podría estar vacío, lo cual es una práctica segura de Rust para evitar errores de índice.
N° 66