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 fuenteiter, el elementosepque se insertará, y un estado booleanosiguiente_es_seppara manejar la máquina de estados interna.Intercalar::new: Constructor que inicializa el estadosiguiente_es_sepenfalsepara 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. Sisiguiente_es_sepestrue, devuelve una copia deself.sep(requiereClone) y reinicia la bandera. Si esfalse, intenta obtener un elemento deself.iter. Si tiene éxito, marcasiguiente_es_sepcomotruepara la siguiente iteración.size_hint: Calcula la capacidad necesaria. Si el iterador original tieneNelementos, el nuevo tendrá2N - 1. Se utilizahigh.mappara manejar correctamente el caso donde el tamaño superior esNone(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 implementeIteratoradquiera los métodos definidos enIntercalarExtsin 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 unIntercalar<std::vec::IntoIter<...>>, el cual es recolectado mediante.collect()en un nuevoVec.
N° 121