Iteradores avanzados en Rust: map, filter, fold, chain, zip, flat_map y take

Los iteradores de Rust son lazy: no hacen nada hasta que los consumes. Ese diseño permite encadenar adaptadores sin crear colecciones intermedias, y el compilador optimiza la cadena completa hasta código equivalente a un bucle manual. Los artículos anteriores introdujeron map, filter y collect. Aquí veremos los adaptadores más útiles que van más allá de lo básico.

fold: acumular un resultado

fold es el adaptador más general. Cualquier iterador puede expresarse con él. Recibe un valor inicial y un closure que recibe el acumulador y el elemento actual:

fn main() {
    let suma = (1..=5).fold(0, |acc, x| acc + x);
    println!("{suma}"); // 15

    // Producto de una lista
    let producto = vec![1, 2, 3, 4, 5].iter().fold(1, |acc, x| acc * x);
    println!("{producto}"); // 120

    // Construir un String desde un iterador
    let palabras = vec!["hola", "mundo", "rust"];
    let frase = palabras.iter().fold(String::new(), |mut acc, w| {
        if !acc.is_empty() { acc.push(' '); }
        acc.push_str(w);
        acc
    });
    println!("{frase}"); // hola mundo rust
}

chain: concatenar iteradores

fn main() {
    let a = vec![1, 2, 3];
    let b = vec![4, 5, 6];

    let concatenado: Vec<_> = a.iter().chain(b.iter()).collect();
    println!("{:?}", concatenado); // [1, 2, 3, 4, 5, 6]

    // Útil para combinar iteradores de distintas fuentes
    let errores = vec!["timeout", "connection refused"];
    let advertencias = vec!["deprecated", "slow query"];

    let todos: Vec<_> = errores.iter()
        .chain(advertencias.iter())
        .filter(|&&s| s.len() > 7)
        .collect();
    println!("{todos:?}"); // ["timeout", "connection refused", "deprecated", "slow query"]
}

zip: combinar dos iteradores en pares

fn main() {
    let nombres = vec!["Ana", "Luis", "Eva"];
    let puntos  = vec![95, 87, 91];

    let ranking: Vec<_> = nombres.iter().zip(puntos.iter()).collect();
    for (nombre, pts) in &ranking {
        println!("{nombre}: {pts}");
    }
    // Ana: 95 / Luis: 87 / Eva: 91

    // zip se detiene en el iterador más corto
    let corto  = vec![1, 2, 3];
    let largo  = vec![10, 20, 30, 40, 50];
    let pares: Vec<_> = corto.iter().zip(largo.iter()).collect();
    println!("{pares:?}"); // [(1, 10), (2, 20), (3, 30)]
}

flat_map: mapear y aplanar

fn main() {
    // Cada pedido tiene varios productos; queremos todos en una lista plana
    let pedidos = vec![
        vec!["manzana", "pan"],
        vec!["leche"],
        vec!["café", "azúcar", "galletas"],
    ];

    let productos: Vec<_> = pedidos.iter().flat_map(|p| p.iter()).collect();
    println!("{productos:?}");
    // ["manzana", "pan", "leche", "café", "azúcar", "galletas"]

    // flat_map equivale a map + flatten
    let numeros = vec![1, 2, 3];
    let expandido: Vec<_> = numeros.iter()
        .flat_map(|&x| vec![x, x * 10])
        .collect();
    println!("{expandido:?}"); // [1, 10, 2, 20, 3, 30]
}

enumerate: índice junto al valor

fn main() {
    let frutas = vec!["manzana", "pera", "naranja"];

    for (i, fruta) in frutas.iter().enumerate() {
        println!("{i}: {fruta}");
    }
    // 0: manzana / 1: pera / 2: naranja

    // Buscar el índice de un elemento
    let pos = frutas.iter().enumerate()
        .find(|(_, &f)| f == "pera")
        .map(|(i, _)| i);
    println!("pera en posición: {pos:?}"); // Some(1)
}

take y skip: paginación

fn main() {
    let articulos: Vec<i32> = (1..=100).collect();
    let pagina = 2;
    let por_pagina = 10;

    let resultado: Vec<_> = articulos.iter()
        .skip((pagina - 1) * por_pagina)
        .take(por_pagina)
        .collect();

    println!("{resultado:?}"); // [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
}

El error típico con lazy evaluation

fn main() {
    let _cadena = vec!["a", "b", "c"]
        .iter()
        .map(|s| { println!("procesando {s}"); s.to_uppercase() });
    // No imprime nada: el iterador no se ha consumido

    // Necesitas collect(), for_each(), sum(), etc. para ejecutarlo
    let resultado: Vec<_> = vec!["a", "b", "c"]
        .iter()
        .map(|s| { println!("procesando {s}"); s.to_uppercase() })
        .collect(); // Ahora sí ejecuta

    println!("{resultado:?}");
}

Resumen

  • fold generaliza cualquier acumulación: suma, producto, strings, estructuras personalizadas.
  • chain encadena iteradores sin asignar memoria intermedia.
  • zip combina dos iteradores en pares y se detiene en el más corto.
  • flat_map mapea y aplana en un solo paso, ideal para listas de listas.
  • enumerate añade el índice sin romper la cadena.
  • take y skip permiten paginación limpia sin cortes manuales.
  • Los iteradores son lazy: sin un consumidor final no hacen nada.

COMPARTE ESTE ARTÍCULO

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