Iteradores e Ownership (Posse)

O modelo de ownership do Rust afeta muitas APIs. Um exemplo disso são os traits Iterator e IntoIterator.

Iterator (Iterador)

Os traits são como interfaces: eles descrevem o comportamento (métodos) para um tipo. O trait Iterator simplesmente diz que você pode chamar next até obter None como retorno:

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

Você usa esse trait da seguinte 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!("Sem mais itens: {:?}", iter.next());
}

Qual é o tipo retornado pelo iterador? Teste sua resposta aqui:

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

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

Por que esse tipo?

IntoIterator

O trait Iterator informa como iterar depois de criar um iterador. O trait relacionado IntoIterator lhe informa como criar o iterador:

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

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

A sintaxe aqui significa que toda implementação de IntoIterator deve declarar dois tipos:

  • Item: o tipo sobre o qual iteramos, como i8,
  • IntoIter: o tipo Iterator retornado pelo método into_iter.

Observe que IntoIter e Item estão vinculados: o iterador deve ter o mesmo tipo Item, o que significa que ele retorna Option<Item>

Como antes, qual é o tipo retornado pelo 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:?}");
}

Loops for

Agora que conhecemos Iterator e IntoIterator, podemos construir loops for. Eles chamam into_iter() em uma expressão e itera sobre o iterador resultante:

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

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

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

Qual é o tipo de palavra em cada laço?

Experimente com o código acima e depois consulte a documentação para impl IntoIterator para &Vec<T> e impl IntoIterator para Vec<T> para verificar suas respostas.