Rust: Iteradores em Rust

Última atualização: 2026-08-26

Um iterador é o “pipeline de processamento de dados preguiçoso” do Rust — ele não calcula os resultados imediatamente, mas produz os elementos um por um, permitindo que você processe sequências de dados de forma declarativa por meio de chamadas encadeadas.

Um iterador é como uma linha de montagem de fábrica: os dados entram por uma extremidade, passam por uma série de processos (filtragem, transformação, extração, agregação) e, por fim, são produzidos como um produto acabado na outra extremidade. Cada processo realiza apenas uma tarefa, mas, quando combinados, eles podem realizar tarefas de processamento complexas.


1. A história de uma fábrica com linha de montagem

(1) Dificuldade: Processar dados com loops é tedioso e demorado

Xiao Ming é o supervisor da linha de montagem da fábrica da Rust. Ele precisa processar um lote de dados de peças:

  1. Separe todos os produtos qualificados (itens com números pares)
  2. Marque cada parte duas vezes
  3. Considere apenas os primeiros 5
  4. Totais estatísticos

Ele escreveu isso usando um loop tradicional for:

RUST
fn main() {
    let parts = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    let mut result = vec![];
    let mut count = 0;

    for &part in &parts {
        if part % 2 == 0 {           // Steps1: Filter
            let doubled = part * 2;  // Steps2: Convert
            result.push(doubled);
            count += 1;
            if count == 5 {          // Steps3: Excerpt
                break;
            }
        }
    }

    let sum: i32 = result.iter().sum();
    println!("Result: {:?}, Sum: {}", result, sum);
}

Embora o código funcione, a lógica está espalhada por todo o programa. Se o requisito mudar para “pular os dois primeiros novamente” ou “selecionar um número par de itens novamente”, seria necessário reescrever todo o loop.

(2) A abordagem do pipeline de iteradores

Reescreva a lógica acima usando chamadas encadeadas de iteradores:

RUST
fn main() {
    let parts = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    let sum: i32 = parts.iter()
        .filter(|&&n| n % 2 == 0)   // Steps1: Filter for Even Numbers
        .map(|&n| n * 2)             // Steps2: multiply by2
        .take(5)                     // Steps3: Take the first 5
        .sum();                      // Steps4: Sum

    println!("Sum: {}", sum);
}

O código passou de “como” para “o quê”. Cada linha representa um processo independente que pode ser inserido, excluído ou reordenado a qualquer momento — assim como o reajuste das estações de trabalho em uma linha de montagem.


2. Diagramas conceituais

O diagrama Mermaid a seguir ilustra o processo completo de avaliação preguiçosa da cadeia de métodos da característica Iterator, de iter() a collect():

100%
graph LR
    A["Raw Data<br/>1..=20"] --> B["iter()<br/>Create an iterator"]
    B --> C["filter(|n| n%2==0)<br/>Filter for Even Numbers"]
    C --> D["map(|n| n*3)<br/>Each element ×3"]
    D --> E["skip(2)<br/>Skip the first 2"]
    E --> F["take(5)<br/>Take the first 5"]
    F --> G["collect()<br/>Consumer: Triggered Evaluation"]

    H["Lazy Evaluation: Build the pipeline only<br/>Do not calculate immediately"] -.-> C
    H -.-> D
    H -.-> E
    H -.-> F
    I["Consumer-Driven Execution<br/>Produce the final result"] -.-> G

    G --> J["Results<br/>[18, 24, 30, 36, 42]"]

3. O que você vai aprender


4. Conceitos fundamentais

100%
graph TB
    A[Iterator Iterator] --> B[Iterator Adapters<br>Adapter]
    A --> C[Consumer Consumer]

    B --> D["map(|x| x+1) Convert"]
    B --> E["filter(|x| x>0) Filter"]
    B --> F["take(n) Take the first n"]
    B --> G["skip(n) Skip n"]
    B --> H["chain(other) Concat"]
    B --> I["enumerate() Add an index"]

    C --> J["collect() Collected into a set"]
    C --> K["sum() Sum"]
    C --> L["count() Count"]
    C --> M["fold(init, fn) Collapse"]
    C --> N["for x in iter Loop"]

    B -.->|"❌ Inertia<br>If it isn't called, it won't run."| C
    C --> O["Triggered Evaluation"]

(1) Adaptadores de iterador x Consumidores

Recurso Adaptador de iterador Consumidor
Função Converter um iterador (de um iterador para outro) Executar o iterador e gerar o resultado final
Valor de retorno Novo tipo para Iterator Valores específicos (por exemplo, Vec<T>, i32, usize)
Preguiçoso Preguiçoso (não executado imediatamente) Avarento (executado imediatamente)
Método típico map, filter, take, skip, chain collect, sum, count, fold, for_each
Posição na cadeia Etapa intermediária Etapa final
Exemplo `.map( x

(2) Referência rápida para adaptadores de iterador comuns

Adaptador Função Exemplo Resultado
map(f) Aplicar a transformação f a cada elemento [1,2,3].iter().map(|x| x*2) 2, 4, 6
filter(p) Manter os elementos que satisfazem a condição p [1..=5].filter(|x| x%2==0) 2, 4
take(n) Selecione apenas os primeiros n elementos [1..].take(3) 1, 2, 3
skip(n) Ignorar os primeiros n elementos [1..=5].skip(2) 3, 4, 5
chain(it) Adicionar outro iterador [1,2].iter().chain([3,4].iter()) 1, 2, 3, 4
enumerate() Atribuir um índice a cada elemento (i, val) ['a','b'].iter().enumerate() (0,'a'), (1,'b')
zip(it) Emparelhe os dois iteradores, um por um [1,2].iter().zip(['a','b'].iter()) (1,'a'), (2,'b')
rev() Iterador reverso [1..=3].rev() 3, 2, 1

(3) Referência rápida aos métodos comuns de consumo

Consumidor Efeito Exemplo Tipo de retorno É ganancioso
collect() Colecionar como conjunto iter.collect::<Vec<_>>() B: FromIterator Sim
sum() Soma iter.sum::<i32>() S: Sum Sim
count() contagem iter.count() usize Sim
fold(init, f) Cálculo cumulativo iter.fold(0, |acc, x| acc + x) Tipo de valor inicial Sim
reduce(f) Colapso sem valor inicial iter.reduce(|a, b| a + b) Option<Item> Sim
for_each(f) Executar um por um (sem valor de retorno) iter.for_each(|x| println!(x)) () Sim
any(p) Existe alguma correspondência? iter.any(|x| x > 0) bool Sim
all(p) Todas as condições foram atendidas? iter.all(|x| x > 0) bool Sim
find(p) Encontre o primeiro que atenda aos critérios iter.find(|x| *x > 3) Option<Item> Sim
max() / min() Máx./Mín. iter.max() Option<Item> Sim

5. Exemplos

▶ Exemplo 1: A característica Iterator e o método next() — Compreendendo a essência dos iteradores (Dificuldade ⭐)

RUST
// ============================================
// Manual Invocation next() Understanding How Iterators Work
// ============================================

fn main() {
    let numbers = vec![10, 20, 30, 40, 50];

    // iter() Returns an iterator,Does not consume vectors
    let mut iter = numbers.iter();

    // next() Every time it returns Option<&T>
    // Some(&value) Indicates that there is another element
    // None Indicates the end of the iteration
    println!("{:?}", iter.next());  // Some(10)
    println!("{:?}", iter.next());  // Some(20)
    println!("{:?}", iter.next());  // Some(30)
    println!("{:?}", iter.next());  // Some(40)
    println!("{:?}", iter.next());  // Some(50)
    println!("{:?}", iter.next());  // None
    println!("{:?}", iter.next());  // None (A subsequent call still returns None)

    // for A loop is next() syntactic sugar
    let mut count = 0;
    let iter2 = numbers.iter();
    for val in iter2 {
        println!("for loop #{}: {}", count, val);
        count += 1;
    }
}

Resultado:

TEXT 📖 Somente leitura
Some(10)
Some(20)
Some(30)
Some(40)
Some(50)
None
None
for loop #0: 10
for loop #1: 20
for loop #2: 30
for loop #3: 40
for loop #4: 50

O princípio fundamental de um iterador é o método next(): cada chamada retorna Some(element) e, quando esgotado, retorna None. O loop for é um recurso sintático que chama repetidamente next() até encontrar None. Compreender isso significa compreender os fundamentos de todos os iteradores.


▶ Exemplo 2: Encadeamento de adaptadores de iterador — map / filter / take / skip (Dificuldade: ⭐⭐)

RUST
// ============================================
// Combine Multiple Adapters to Build a Data Processing Pipeline
// ============================================

fn main() {
    // Raw Data: 1 to 20
    let data = 1..=20;

    // Pipeline: Filter for Even Numbers → multiply by 3 → Skip the first 2 → Take the first 5
    let result: Vec<i32> = data
        .filter(|&n| n % 2 == 0)      // [2,4,6,8,10,12,14,16,18,20]
        .map(|n| n * 3)                // [6,12,18,24,30,36,42,48,54,60]
        .skip(2)                       // [18,24,30,36,42]
        .take(5)                       // [18,24,30,36,42]
        .collect();                    // Triggered Evaluation,Collected Vec

    println!("Pipeline result: {:?}", result);

    // Another pipeline: use chain to Concat two slices
    let first = vec!["A", "B", "C"];
    let second = vec!["X", "Y", "Z"];
    let combined: Vec<&str> = first.iter()
        .chain(second.iter())
        .copied()
        .collect();
    println!("Chained: {:?}", combined);

    // Use enumerate to index elements
    let fruits = vec!["apple", "banana", "cherry"];
    let indexed: Vec<(usize, &str)> = fruits.iter()
        .enumerate()
        .map(|(i, &name)| (i + 1, name))
        .collect();
    println!("Indexed: {:?}", indexed);
}

Resultado:

TEXT 📖 Somente leitura
Pipeline result: [18, 24, 30, 36, 42]
Chained: ["A", "B", "C", "X", "Y", "Z"]
Indexed: [(1, "apple"), (2, "banana"), (3, "cherry")]

Uma cadeia de adaptadores é como a disposição das estações de trabalho em uma linha de montagem: cada adaptador realiza apenas uma tarefa, e os dados fluem sequencialmente por cada estação de trabalho. collect() é o sinal de demanda no final do pipeline — sem ele, os trabalhadores no pipeline não começam a trabalhar (avaliação preguiçosa).


▶ Exemplo 3: Iteradores personalizados — Implementando a característica Iterator para o seu tipo (Dificuldade: ⭐⭐)

RUST
// ============================================
// Custom Fibonacci Iterator
// Implementation Iterator trait Make any type iterable
// ============================================

// Fibonacci Sequence Generator
struct Fibonacci {
    current: u64,
    next: u64,
    max: u64,
}

impl Fibonacci {
    fn new(max: u64) -> Self {
        Fibonacci {
            current: 0,
            next: 1,
            max,
        }
    }
}

// Implementation Iterator trait It is the core of custom iterators.
impl Iterator for Fibonacci {
    // Item The type of the elements returned by the iterator
    type Item = u64;

    // next() Back Option<Self::Item>
    // Some(value) Indicates that there is another element
    // None Indicates the end of the iteration
    fn next(&mut self) -> Option<Self::Item> {
        if self.current > self.max {
            return None;
        }
        let result = self.current;

        // Update to the next Fibonacci number
        let new_next = self.current + self.next;
        self.current = self.next;
        self.next = new_next;

        Some(result)
    }
}

fn main() {
    println!("Fibonacci up to 50:");
    let fib = Fibonacci::new(50);

    // Fibonacci It can now be used in for In a loop
    for (i, n) in fib.enumerate() {
        println!("  fib({}) = {}", i, n);
    }

    // It can also be used in conjunction with an adapter chain
    println!("\nEven Fibonacci numbers up to 100:");
    let even_fibs: Vec<u64> = Fibonacci::new(100)
        .filter(|&n| n % 2 == 0)
        .collect();
    println!("{:?}", even_fibs);
}

Resultado:

TEXT 📖 Somente leitura
Fibonacci up to 50:
  fib(0) = 0
  fib(1) = 1
  fib(2) = 1
  fib(3) = 2
  fib(4) = 3
  fib(5) = 5
  fib(6) = 8
  fib(7) = 13
  fib(8) = 21
  fib(9) = 34

Even Fibonacci numbers up to 100:
[0, 2, 8, 34]

Para implementar Iterator trait, basta fazer uma coisa: definir type Item (tipo de elemento) e fn next() (regra de produção). Uma vez implementado, seu tipo ganha automaticamente todos os métodos adaptadores (map, filter, take, etc.) — essa é a concretização do conceito de “duck typing” no Rust: se você implementar next(), ele poderá ser usado exatamente como um iterador.


▶ Exemplo 4: O consumidor em ação — dobrar / somar / contar / coletar (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// Consumer:Drive the iterator to execute and produce the final result
// ============================================

fn main() {
    let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    // sum() — Sum
    let total: i32 = numbers.iter().sum();
    println!("Sum: {}", total);

    // count() — Count
    let cnt = numbers.iter().count();
    println!("Count: {}", cnt);

    // fold() — General Folding Operation (initial value + accumulator closure)
    // Here, we calculate 10!
    let factorial: u64 = (1..=10u64).fold(1, |acc, x| acc * x);
    println!("10! = {}", factorial);

    // fold() — Manual Implementation of sum and count
    let sum_via_fold: i32 = numbers.iter().fold(0, |acc, &x| acc + x);
    let count_via_fold: usize = numbers.iter().fold(0, |acc, _| acc + 1);
    println!("Sum (via fold): {}", sum_via_fold);
    println!("Count (via fold): {}", count_via_fold);

    // collect() — Collected various types of collections
    let doubled: Vec<i32> = numbers.iter().map(|&x| x * 2).collect();
    println!("Doubled: {:?}", doubled);

    let even_set: std::collections::HashSet<i32> = numbers.iter()
        .filter(|&&x| x % 2 == 0)
        .copied()
        .collect();
    println!("Even set: {:?}", even_set);

    // General: find the sum of the first 5 even squares
    let complex_result: i32 = (1..=100)
        .filter(|&n| n % 2 == 0)
        .map(|n| n * n)
        .take(5)
        .fold(0, |acc, n| acc + n);
    println!("Sum of first 5 even squares: {}", complex_result);
}

Resultado:

TEXT 📖 Somente leitura
Sum: 55
Count: 10
10! = 3628800
Sum (via fold): 55
Count (via fold): 10
Doubled: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
Even set: {2, 4, 6, 8, 10}
Sum of first 5 even squares: 220

Os consumidores são os componentes-chave no final do pipeline: sum(), count() e fold() calculam diretamente os resultados numéricos; collect() coleta dados em um conjunto. fold() é o consumidor de uso mais geral — sum() e count() são, essencialmente, formas especializadas de fold(). Um pipeline precisa ter um consumidor para ser efetivamente executado; caso contrário, tudo não passa de conversa.


▶ Exemplo 5: Exercício abrangente — Implementação de um iterador personalizado para criar um gerador de FizzBuzz (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// Comprehensive Example:Custom Iterators + Adapter Chain
// ============================================

struct FizzBuzz {
    current: u32,
    limit: u32,
}

impl FizzBuzz {
    fn new(limit: u32) -> Self {
        FizzBuzz { current: 0, limit }
    }
}

impl Iterator for FizzBuzz {
    type Item = String;

    fn next(&mut self) -> Option<String> {
        self.current += 1;
        if self.current > self.limit {
            return None;
        }
        let n = self.current;
        let result = match (n % 3, n % 5) {
            (0, 0) => "FizzBuzz".to_string(),
            (0, _) => "Fizz".to_string(),
            (_, 0) => "Buzz".to_string(),
            _ => n.to_string(),
        };
        Some(result)
    }
}

struct Fibonacci {
    curr: u64,
    next: u64,
}

impl Iterator for Fibonacci {
    type Item = u64;
    fn next(&mut self) -> Option<u64> {
        let result = self.curr;
        self.curr = self.next;
        self.next = result + self.next;
        Some(result)
    }
}

fn main() {
    println!("=== FizzBuzz (1-20) ===");
    for item in FizzBuzz::new(20) {
        print!("{} ", item);
    }
    println!();

    let fizz_count = FizzBuzz::new(100)
        .filter(|s| s.starts_with("Fizz"))
        .count();
    println!("1-100 Fizz occurrences: {}", fizz_count);

    println!("\n=== Fibonacci first 15 terms ===");
    let fib = Fibonacci { curr: 0, next: 1 };
    for val in fib.take(15) {
        print!("{} ", val);
    }
    println!();

    let fib_sum: u64 = Fibonacci { curr: 1, next: 1 }
        .take_while(|&x| x < 1_000_000)
        .filter(|&x| x % 2 == 0)
        .sum();
    println!("Fibonacci < 1M sum of even numbers: {}", fib_sum);
}

Resultado:

TEXT 📖 Somente leitura
=== FizzBuzz (1-20) ===
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz 

1-100 Fizz occurrences: 27

=== Fibonacci first 15 terms ===
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 

Fibonacci < 1M sum of even numbers: 1089154

Para criar um iterador personalizado, basta implementar o método next() da característica Iterator. FizzBuzz Os geradores podem ser encadeados usando filter/count; Fibonacci os iteradores infinitos podem limitar sua saída usando take/take_while e, em seguida, agregar os resultados usando filter/sum.


❓ Perguntas Frequentes

P: Qual é a diferença entre iter(), into_iter() e iter_mut()? R: Elas retornam tipos diferentes de iteradores. iter() retorna &T (uma referência imutável) sem transferir a propriedade; into_iter() retorna T (transferência de propriedade), o que consome a coleção original; iter_mut() retorna &mut T (referência mutável), o que permite que os elementos sejam modificados. O loop for usa into_iter() por padrão.

P: Por que a cadeia de adaptadores não gera um erro, mesmo sem executar nenhum código? R: Porque os adaptadores são preguiçosos. Os adaptadores apenas criam um “plano de operação” sem executá-lo. É como ter todas as máquinas instaladas em uma fábrica, mas desligadas — elas só começam a funcionar quando você chama um consumidor (collect(), sum(), etc.). Essa é uma característica central do design de iteradores do Rust: abstração sem sobrecarga que realiza cálculos apenas quando realmente necessário.

P: collect() Como sei qual tipo coletar? R: Por meio da inferência de tipo. É preciso especificar o tipo de destino, geralmente usando a sintaxe do TurboFish: .collect::<Vec<i32>>() ou declarando o tipo da variável let v: Vec<i32> = iter.collect();. O compilador determina como coletar com base na implementação do tipo de destino FromIterator.

P: O que é type Item em um iterador personalizado? R: Item é um tipo associado que especifica o tipo de Some retornado por next(). Por exemplo, type Item = u64 em Iterator for Fibonacci indica que next() retorna Option<u64> a cada vez. Os tipos associados permitem que você especifique o tipo dos elementos produzidos por um iterador sem a necessidade de parâmetros genéricos adicionais.

P: Qual é a diferença entre fold() e reduce()? R: fold() requer um valor inicial, enquanto reduce() usa o primeiro elemento como valor inicial. fold(0, \|acc, x\| acc + x) começa a contar a partir de 0; reduce(\|acc, x\| acc + x) começa a contar a partir do primeiro elemento. fold() sempre retorna o tipo de valor inicial que você especificar, enquanto reduce() retorna Option<Self::Item> (ou None se o iterador estiver vazio).


📖 Resumo


📝 Exercícios

  1. Dificuldade ⭐: Reescreva o código a seguir usando iteradores. Extraia todos os números ímpares de [1, 2, 3, 4, 5, 6, 7, 8], multiplique-os por 10, armazene-os em um Vec e imprima-os.

    RUST
    // Replace this loop with a chain of iterators
    let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8];
    let mut result = vec![];
    for &n in &numbers {
        if n % 2 == 1 {
            result.push(n * 10);
        }
    }
    println!("{:?}", result);
    
  2. Dificuldade ⭐⭐: Implemente a característica Iterator para struct StepRange { start: i32, end: i32, step: i32 } de forma que ela possa ser usada como for n in StepRange::new(0, 10, 2), gerando os valores 0, 2, 4, 6, 8 e 10. Em seguida, use map para elevar cada valor ao quadrado e use collect para reuni-los em um Vec.

  3. Dificuldade ⭐⭐⭐: Escreva uma função fn word_count(text: &str) -> std::collections::HashMap<String, usize> que utilize métodos de iterador para contar o número de vezes que cada palavra aparece em um texto. Requisitos: Use split_whitespace() para dividir o texto, map para converter para letras minúsculas e fold para construir um HashMap. Dica: a API entry() do HashMap, combinada com or_insert(), facilita a contagem.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%