Un adaptador de iterador es un struct que envuelve a un iterador existente para transformar sus elementos o cambiar su comportamiento de iteración. A diferencia de los adaptadores estándar como map o filter, que devuelven tipos genéricos ya definidos, crear uno propio permite encapsular una lógica de transformación compleja manteniendo la eficiencia y la interoperabilidad con el ecosistema de iteradores de Rust.

Para que un adaptador sea eficiente y correcto, debe implementar el trait Iterator y, idealmente, otros traits de optimización como size_hint, DoubleEndedIterator o ExactSizeIterator. Un error común al diseñar adaptadores es no propagar correctamente la información de tamaño en size_hint, lo que impide que métodos como collect optimicen la asignación de memoria.

Para añadir métodos personalizados a los iteradores existentes sin violar la regla de huérfanos (que prohíbe implementar un trait externo en un tipo externo), se utiliza el patrón de Extension Trait. En lugar de intentar implementar IteratorExt para Iterator, definimos nuestro propio trait IntercalarExt y lo implementamos para cualquier tipo I que ya sea un Iterator.

/// Un adaptador que intercala un elemento separador entre los elementos de un iterador.
struct Intercalar<I> {
    iter: I,
    sep: I::Item,
    siguiente_es_sep: bool,
}

impl<I: Iterator> Intercalar<I> {
    /// Crea un nuevo adaptador Intercalar.
    fn new(iter: I, sep: I::Item) -> Self {
        Self {
            iter,
            sep,
            siguiente_es_sep: false,
        }
    }
}

impl<I: Iterator> Iterator for Intercalar<I>
where
    I::Item: Clone,
{
    type Item = I::Item;

    fn next(&mut self) -> Option<Self::Item> {
        // Si la bandera indica que el próximo elemento debe ser el separador
        if self.siguiente_es_sep {
            self.siguiente_es_sep = false;
            return Some(self.sep.clone());
        }

        // Intentar obtener el siguiente elemento del iterador fuente
        match self.iter.next() {
            Some(val) => {
                // Si obtuvimos un valor, el próximo llamado debe devolver el separador
                self.siguiente_es_sep = true;
                Some(val)
            }
            None => None, // El iterador original se ha agotado
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let (low, high) = self.iter.size_hint();
        if low == 0 {
            return (0, 0);
        }
        // Si hay N elementos, habrá (N-1) separadores. Total: 2N - 1
        let low_ext = 2 * low - 1;
        let high_ext = high.map(|h| 2 * h - 1);
        (low_ext, high_ext)
    }
}

/// Trait de extensión para permitir el uso de `.intercalar()` en cualquier iterador.
trait IntercalarExt: Iterator + Sized {
    fn intercalar(self, sep: Self::Item) -> Intercalar<Self>
    where
        Self::Item: Clone,
    {
        Intercalar::new(self, sep)
    }
}

// Implementación del trait de extensión para todos los tipos que implementen Iterator
impl<I: Iterator> IntercalarExt for I {}

fn main() {
    let palabras = vec!["Rust", "es", "genial"];
    
    // Uso del adaptador personalizado mediante el método de extensión
    let resultado: Vec<&str> = palabras
        .into_iter()
        .intercalar("-")
        .collect();

    println!("{:?}", resultado); // ["Rust", "-", "es", "-", "genial"]
}

Explicación del Código

  • Intercalar<I>: Es la estructura principal del adaptador. Contiene el iterador fuente iter, el elemento sep que se insertará, y un estado booleano siguiente_es_sep para manejar la máquina de estados interna.
  • Intercalar::new: Constructor que inicializa el estado siguiente_es_sep en false para asegurar que el primer elemento sea siempre del iterador original y no el separador.
  • impl<I: Iterator> Iterator for Intercalar<I>: Implementación del comportamiento de iteración.
    • type Item = I::Item: El tipo de elemento del adaptador es el mismo que el del iterador original.
    • next: Implementa la lógica de intercala. Si siguiente_es_sep es true, devuelve una copia de self.sep (requiere Clone) y reinicia la bandera. Si es false, intenta obtener un elemento de self.iter. Si tiene éxito, marca siguiente_es_sep como true para la siguiente iteración.
    • size_hint: Calcula la capacidad necesaria. Si el iterador original tiene N elementos, el nuevo tendrá 2N - 1. Se utiliza high.map para manejar correctamente el caso donde el tamaño superior es None (desconocido).
  • IntercalarExt: Este es el “Extension Trait”. Al definirlo, permitimos que cualquier iterador tenga el método .intercalar().
  • impl<I: Iterator> IntercalarExt for I {}: Esta implementación es la clave para el “Fluent API”. Permite que cualquier tipo que implemente Iterator adquiera los métodos definidos en IntercalarExt sin violar la regla de huérfanos, ya que estamos implementando nuestro propio trait en un tipo genérico.
  • main: Demuestra el uso final. El método .intercalar("-") devuelve un Intercalar<std::vec::IntoIter<...>>, el cual es recolectado mediante .collect() en un nuevo Vec.

121