iter vs into_iter em Rust: o que está por baixo do capô?

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

iter vs into_iter: batalha dos titãs da iteração em Rust

\n\n

Se você já escreveu um loop sobre um vetor em Rust, certamente já encontrou os três irmãos gêmeos: iter(), into_iter() e iter_mut(). À primeira vista, todos fazem a mesma coisa — permitem percorrer os elementos. Mas a diferença entre eles é mais profunda do que parece, e está no coração do Rust — o sistema de propriedade.

\n\n

Imagine que você é um barista e sua coleção são os grãos de café. iter() permite que você olhe para os grãos, os cheire, mas não os toque. into_iter() é quando você pega os grãos, os moe e faz um espresso, após o que os grãos desaparecem. E iter_mut() — você pode não apenas olhar, mas também moer os grãos diretamente no saco, mudando sua forma.

\n\n

Hoje vamos analisar iter vs into_iter da forma como é explicado no Stack Overflow, mas com exemplos vivos e um mergulho profundo no capô. Vamos lá!

\n\n

O que é um iterador em Rust?

\n\n

Um iterador é um objeto que sabe como obter elementos sequencialmente de uma coleção. Em Rust, os iteradores são preguiçosos: eles não fazem nada até que você chame next(). Mas o principal é que cada método de iteração lida com a propriedade dos dados de forma diferente.

\n\n

Aqui estão as três principais abordagens:

\n
    \n
  • iter() — toma emprestado os elementos (não assume a propriedade)
  • \n
  • into_iter() — consome a coleção (assume a propriedade)
  • \n
  • iter_mut() — toma emprestado com possibilidade de modificação
  • \n
\n\n

Vamos olhar o código. Suponha que temos um vetor de nomes:

\n\n
let names = vec!["Ana".to_string(), "Boris".to_string(), "Viktor".to_string()];
\n\n

Exemplo 1: iter() — apenas olhar

\n\n
fn main() {\n    let names = vec!["Ana".to_string(), "Boris".to_string(), "Viktor".to_string()];\n    \n    // iter() retorna &String (referência)\n    for name in names.iter() {\n        println!("Olá, {}!", name);\n    }\n    \n    // names ainda está disponível!\n    println!("Vetor vivo: {:?}", names);\n}
\n\n

Observe: após o loop, usamos names tranquilamente. Por quê? Porque iter() retornou uma referência para cada elemento. A propriedade permaneceu com o vetor. Isso é seguro e previsível.

\n\n

Exemplo 2: into_iter() — queimar as pontes

\n\n
fn main() {\n    let names = vec!["Ana".to_string(), "Boris".to_string(), "Viktor".to_string()];\n    \n    // into_iter() retorna String (propriedade é movida)\n    for name in names.into_iter() {\n        println!("Adeus, {}!", name);\n    }\n    \n    // Erro! names não existe mais\n    // println!("Vetor vivo: {:?}", names); // ❌ borrow of moved value\n}
\n\n

Aqui está a diferença chave. into_iter() consome a coleção. Cada elemento é movido para o loop, e o vetor original se torna inválido. Se você precisa manter a coleção para uso posterior — into_iter() não é sua escolha.

\n\n

Exemplo 3: iter_mut() — modificar sem destruir

\n\n
fn main() {\n    let mut names = vec!["Ana".to_string(), "Boris".to_string(), "Viktor".to_string()];\n    \n    // iter_mut() retorna &mut String (referência mutável)\n    for name in names.iter_mut() {\n        name.push_str(" (convidado)");\n    }\n    \n    println!("Vetor modificado: {:?}", names);\n    // Saída: ["Ana (convidado)", "Boris (convidado)", "Viktor (convidado)"]\n}
\n\n

Aqui, modificamos os elementos no local, mas não assumimos a propriedade. Ideal para transformações.

\n\n

Sob o capô: o que cada método retorna?

\n\n

Para entender verdadeiramente iter vs into_iter, vamos dar uma olhada na biblioteca padrão. Para o vetor Vec<T>:

\n\n