O que é o Trait Iterator
O trait Iterator é a base da programação funcional e do processamento de coleções em Rust. Qualquer tipo que implemente Iterator precisa definir apenas um método: next(), que retorna Option<Self::Item> — Some(valor) para o próximo elemento ou None quando a iteração termina. A partir disso, dezenas de métodos adaptadores ficam disponíveis automaticamente.
Iteradores em Rust são preguiçosos (lazy): adaptadores como map e filter não fazem nada até que um consumidor como collect, for_each, sum ou fold solicite os valores. Essa abordagem elimina alocações intermediárias e permite ao compilador otimizar cadeias inteiras de transformações em loops eficientes — frequentemente com desempenho igual ou superior ao de loops manuais.
Métodos Mais Usados
fn main() {
let numeros = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// iter() - referências imutáveis
// into_iter() - consome a coleção
// iter_mut() - referências mutáveis
// Demonstração básica com next()
let mut iter = numeros.iter();
assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));
println!("{numeros:?}"); // numeros ainda existe (iter() empresta)
}
Tabela de Métodos Principais
Adaptadores (preguiçosos — retornam outro iterador)
| Método | Descrição | Exemplo |
|---|---|---|
map(f) | Transforma cada elemento | .map(|x| x * 2) |
filter(f) | Mantém elementos onde f é true | .filter(|x| **x > 3) |
filter_map(f) | map + filter (descarta None) | .filter_map(|x| x.parse().ok()) |
flat_map(f) | map + flatten | .flat_map(|x| vec![x, x*10]) |
flatten() | Achata iterador de iteradores | vec![vec![1,2], vec![3]].into_iter().flatten() |
enumerate() | Adiciona índice (i, val) | .enumerate() |
zip(outro) | Combina dois iteradores em tuplas | .zip(outro_iter) |
chain(outro) | Concatena dois iteradores | .chain(outro_iter) |
take(n) | Pega os primeiros n | .take(5) |
skip(n) | Pula os primeiros n | .skip(2) |
take_while(f) | Pega enquanto f for true | .take_while(|x| **x < 5) |
skip_while(f) | Pula enquanto f for true | .skip_while(|x| **x < 5) |
peekable() | Permite espiar o próximo sem consumir | .peekable() |
rev() | Inverte (requer DoubleEndedIterator) | .rev() |
cloned() | Clona referências &T → T | .cloned() |
copied() | Copia referências &T → T (Copy) | .copied() |
inspect(f) | Executa side-effect sem alterar | .inspect(|x| println!("{x}")) |
step_by(n) | Avança n a cada iteração | .step_by(2) |
Consumidores (executam a iteração)
| Método | Descrição | Exemplo |
|---|---|---|
collect() | Coleta em uma coleção | .collect::<Vec<_>>() |
for_each(f) | Executa f para cada elemento | .for_each(|x| println!("{x}")) |
fold(init, f) | Reduz a um único valor | .fold(0, |acc, x| acc + x) |
reduce(f) | Como fold, sem valor inicial | .reduce(|a, b| a + b) |
sum() | Soma todos os elementos | .sum::<i32>() |
product() | Produto de todos os elementos | .product::<i32>() |
count() | Conta os elementos | .count() |
any(f) | Algum satisfaz f? | .any(|x| *x > 5) |
all(f) | Todos satisfazem f? | .all(|x| *x > 0) |
find(f) | Primeiro que satisfaz f | .find(|x| **x > 5) → Option |
position(f) | Índice do primeiro que satisfaz f | .position(|x| *x > 5) → Option |
min() / max() | Menor / maior elemento | .max() → Option |
min_by_key(f) | Menor por critério | .min_by_key(|x| x.abs()) |
last() | Último elemento | .last() → Option |
nth(n) | N-ésimo elemento | .nth(3) → Option |
unzip() | Separa tuplas em duas coleções | .unzip::<Vec<_>, Vec<_>>() |
Exemplos Práticos
1. Pipeline de Transformação de Dados
#[derive(Debug)]
struct Venda {
produto: String,
valor: f64,
quantidade: u32,
}
fn main() {
let vendas = vec![
Venda { produto: "Notebook".into(), valor: 3500.0, quantidade: 2 },
Venda { produto: "Mouse".into(), valor: 89.90, quantidade: 15 },
Venda { produto: "Teclado".into(), valor: 199.90, quantidade: 8 },
Venda { produto: "Monitor".into(), valor: 1200.0, quantidade: 3 },
Venda { produto: "Webcam".into(), valor: 149.90, quantidade: 5 },
];
// Total de vendas acima de R$ 1000 em receita
let total_alto_valor: f64 = vendas
.iter()
.map(|v| v.valor * v.quantidade as f64)
.filter(|&receita| receita > 1000.0)
.sum();
println!("Receita alto valor: R$ {total_alto_valor:.2}");
// Top 3 produtos por receita
let mut por_receita: Vec<_> = vendas
.iter()
.map(|v| (&v.produto, v.valor * v.quantidade as f64))
.collect();
por_receita.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
println!("\nTop 3 por receita:");
for (produto, receita) in por_receita.iter().take(3) {
println!(" {produto}: R$ {receita:.2}");
}
}
2. Criando um Iterador Customizado
struct Fibonacci {
a: u64,
b: u64,
}
impl Fibonacci {
fn new() -> Self {
Fibonacci { a: 0, b: 1 }
}
}
impl Iterator for Fibonacci {
type Item = u64;
fn next(&mut self) -> Option<Self::Item> {
let resultado = self.a;
let novo = self.a.checked_add(self.b)?; // None se houver overflow
self.a = self.b;
self.b = novo;
Some(resultado)
}
}
fn main() {
// Primeiros 15 números de Fibonacci
let primeiros: Vec<u64> = Fibonacci::new().take(15).collect();
println!("Fibonacci: {primeiros:?}");
// Fibonacci menores que 1000
let menores: Vec<u64> = Fibonacci::new()
.take_while(|&n| n < 1000)
.collect();
println!("Menores que 1000: {menores:?}");
// Soma dos pares de Fibonacci até 4 milhões
let soma_pares: u64 = Fibonacci::new()
.take_while(|&n| n <= 4_000_000)
.filter(|n| n % 2 == 0)
.sum();
println!("Soma dos pares até 4M: {soma_pares}");
}
3. enumerate, zip e chain
fn main() {
let linguagens = vec!["Rust", "Python", "Go", "TypeScript"];
let notas = vec![10, 8, 7, 9];
// enumerate — índice + valor
println!("Rankings:");
for (i, lang) in linguagens.iter().enumerate() {
println!(" {}. {lang}", i + 1);
}
// zip — combinar dois iteradores
println!("\nNotas:");
for (lang, nota) in linguagens.iter().zip(notas.iter()) {
println!(" {lang}: {nota}/10");
}
// chain — concatenar iteradores
let frontend = vec!["React", "Vue"];
let backend = vec!["Actix", "Axum"];
let todos: Vec<&str> = frontend.iter().copied()
.chain(backend.iter().copied())
.collect();
println!("\nFrameworks: {todos:?}");
// unzip — separar tuplas
let pares = vec![(1, "um"), (2, "dois"), (3, "três")];
let (numeros, nomes): (Vec<i32>, Vec<&str>) = pares.into_iter().unzip();
println!("\nNúmeros: {numeros:?}");
println!("Nomes: {nomes:?}");
}
4. fold para Agregações Complexas
fn main() {
let transacoes = vec![100.0, -50.0, 200.0, -30.0, -80.0, 150.0];
// Calcular saldo, total de créditos e débitos em uma passada
let (saldo, creditos, debitos) = transacoes.iter().fold(
(0.0_f64, 0.0_f64, 0.0_f64),
|(saldo, cred, deb), &valor| {
if valor >= 0.0 {
(saldo + valor, cred + valor, deb)
} else {
(saldo + valor, cred, deb + valor.abs())
}
},
);
println!("Saldo: R$ {saldo:.2}");
println!("Total créditos: R$ {creditos:.2}");
println!("Total débitos: R$ {debitos:.2}");
// Construir uma string formatada com fold
let palavras = vec!["Rust", "é", "incrível"];
let frase = palavras.iter().enumerate().fold(
String::new(),
|mut acc, (i, palavra)| {
if i > 0 {
acc.push(' ');
}
acc.push_str(palavra);
acc
},
);
println!("{frase}");
}
5. windows e chunks para Processamento em Blocos
fn main() {
let precos = vec![100.0, 102.0, 98.0, 105.0, 110.0, 108.0, 115.0, 112.0];
// Média móvel de 3 períodos usando windows
let medias_moveis: Vec<f64> = precos
.windows(3)
.map(|janela| janela.iter().sum::<f64>() / janela.len() as f64)
.collect();
println!("Médias móveis: {medias_moveis:.1?}");
// Variação percentual entre dias consecutivos
let variacoes: Vec<f64> = precos
.windows(2)
.map(|par| ((par[1] - par[0]) / par[0]) * 100.0)
.collect();
println!("Variações: {variacoes:.2?}%");
// Processar dados em lotes de 3
let dados: Vec<i32> = (1..=12).collect();
let lotes: Vec<Vec<i32>> = dados
.chunks(3)
.map(|chunk| chunk.to_vec())
.collect();
println!("Lotes de 3: {lotes:?}");
}
Dicas de Performance e Armadilhas
Iteradores são zero-cost: O compilador otimiza cadeias de iteradores para loops simples. Não há alocação intermediária entre
map,filter, etc. Use sem medo.collect()precisa de tipo: O compilador nem sempre consegue inferir o tipo destino. Use turbofish:.collect::<Vec<_>>()ou anote a variável:let v: Vec<_> = ....iter()vsinto_iter():iter()empresta a coleção (retorna&T),into_iter()consome (retornaT). Se precisar dos valores, useinto_iter()ou.iter().copied()para tiposCopy.Cuidado com
filterem referências: O predicado defilterrecebe&&Tquando usado comiter(). Use|&&x|ou|x| **x > 5para desreferenciar.for_eachvsfor: Ambos são equivalentes em performance. Usefor_eachem cadeias de iteradores; useforquando precisar debreak,continueoureturn.peekable()para look-ahead: Quando precisar decidir com base no próximo elemento sem consumi-lo, use.peekable()e o métodopeek().
Veja Também
- Iterar Coleções em Rust — receitas práticas de iteração
- Iteradores em Rust — artigo aprofundado sobre o sistema de iteradores
- Vec<T>: Vetor Dinâmico — a coleção mais iterada
- HashMap<K,V> — iteração sobre pares chave-valor
- Option<T> —
next()retorna Option - Slices (&[T]) — windows, chunks e iteração sobre fatias
- Cheatsheet Rust — referência rápida de sintaxe
- Documentação oficial — std::iter::Iterator