Iteradores y Ownership (Propiedad)

El modelo de ownership de Rust afecta a muchas APIs. Un ejemplo serían los traits Iterator y IntoIterator.

Iterator

Los traits son como las interfaces: describen el comportamiento (métodos) de un tipo. El trait Iterator indica que se puede llamar a next hasta que se obtenga None:

#![allow(unused)]
fn main() {
pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
}
}

Utiliza este trait de la siguiente forma:

fn main() {
    let v: Vec<i8> = vec![10, 20, 30];
    let mut iter = v.iter();

    println!("v[0]: {:?}", iter.next());
    println!("v[1]: {:?}", iter.next());
    println!("v[2]: {:?}", iter.next());
    println!("No more items: {:?}", iter.next());
}

¿Qué tipo devuelve el iterador? Prueba tu respuesta aquí:

fn main() {
    let v: Vec<i8> = vec![10, 20, 30];
    let mut iter = v.iter();

    let v0: Option<..> = iter.next();
    println!("v0: {v0:?}");
}

¿Por qué se usa este tipo?

IntoIterator

El trait Iterator te indica cómo iterar una vez que has creado un iterador. El trait relacionado IntoIterator indica cómo crear el iterador:

#![allow(unused)]
fn main() {
pub trait IntoIterator {
    type Item;
    type IntoIter: Iterator<Item = Self::Item>;

    fn into_iter(self) -> Self::IntoIter;
}
}

La sintaxis aquí significa que cada implementación de IntoIterator debe declarar dos tipos:

  • Item: el tipo sobre el que iteramos, como i8,
  • IntoIter: el tipo Iterator devuelto por el método into_iter.

Ten en cuenta que IntoIter y Item están vinculados: el iterador debe tener el mismo tipo de Item, lo que significa que devuelve Option<Item>.

Al igual que antes, ¿qué tipo devuelve el iterador?

fn main() {
    let v: Vec<String> = vec![String::from("foo"), String::from("bar")];
    let mut iter = v.into_iter();

    let v0: Option<..> = iter.next();
    println!("v0: {v0:?}");
}

Bucles for

Ahora que conocemos Iterator e IntoIterator, podemos crear bucles for. Llaman a into_iter() sobre una expresión e iteran sobre el iterador resultante:

fn main() {
    let v: Vec<String> = vec![String::from("foo"), String::from("bar")];

    for word in &v {
        println!("word: {word}");
    }

    for word in v {
        println!("word: {word}");
    }
}

¿Cuál es el tipo de word en cada bucle?

Experimenta con el código anterior y consulta la documentación sobre impl IntoIterator for &Vec<T> y sobre impl IntoIterator for Vec<T> para comprobar las respuestas.