Introdução
Um loop for explícito costuma ser a forma mais clara de processar uma coleção. O Rust também oferece iteradores: valores que produzem um item por vez e podem ser combinados em um pipeline legível de processamento de dados.
Você manterá um loop funcional como referência, reproduzirá o total dele com iter e sum, escreverá uma pequena closure, transformará as leituras com map, rejeitará leituras inválidas com filter e, por fim, combinará essas operações. Todas as edições serão feitas em um projeto Cargo preparado, para que você possa se concentrar nos novos conceitos do Rust.
Reproduza o total de um loop com um iterador
Nesta etapa, você comparará o loop for preparado com um iterador que calcula o mesmo total.
O projeto está em /home/labex/project/reading-pipeline, e o programa está em src/main.rs. O vec![12, 18, -1, 24, 30] preparado usa a macro vec! como uma forma compacta de criar um vetor contendo esses valores; seu objetivo é equivalente ao de criar um vetor vazio e adicionar cada valor, como no laboratório anterior sobre Vec. O loop existente pega esse vetor por empréstimo com &readings, visita cada &i32 e usa *reading para acessar o inteiro referenciado.
Entre no projeto e execute a versão de referência:
cd /home/labex/project/reading-pipeline
cargo run --quiet
Loop total: 83
O total inclui todas as leituras, inclusive -1. Agora abra o código-fonte:
nano src/main.rs
Substitua o comentário da Etapa 1 por:
let iterator_total: i32 = readings.iter().sum();
println!("Iterator total: {iterator_total}");
readings.iter() cria um iterador que pega cada elemento do vetor por empréstimo. Ele não remove nem consome o vetor. sum() adiciona repetidamente os itens produzidos; a biblioteca padrão do Rust sabe somar inteiros obtidos por empréstimo. A anotação : i32 informa o tipo do resultado que sum deve produzir.
Salve com Ctrl+O, pressione Enter e saia com Ctrl+X. Verifique o código antes de executá-lo:
cargo check
cargo run --quiet
Loop total: 83
Iterator total: 83
As linhas iguais mostram que o loop e o iterador descrevem o mesmo cálculo. O vetor continua disponível para as etapas seguintes porque iter() apenas pegou seus elementos por empréstimo.
Transforme leituras com uma closure e map
Nesta etapa, você definirá uma closure e a aplicará a cada leitura com map.
Uma closure é uma pequena função sem nome que pode ser armazenada em uma variável ou passada diretamente a um adaptador de iterador. Na forma |parameter: Type| expression, os parâmetros ficam entre barras verticais, e a closure retorna o valor da expressão. Aqui, uma closure de calibração adicionará um deslocamento de dois.
Abra o código-fonte:
nano src/main.rs
Substitua o comentário da Etapa 2 por:
let add_offset = |reading: i32| reading + 2;
let adjusted: Vec<i32> = readings.iter().map(|reading| add_offset(*reading)).collect();
println!("Adjusted all: {adjusted:?}");
map chama sua closure uma vez para cada item do iterador e produz os valores retornados. O fluxo de tipos é &i32 de iter() → i32 copiado de *reading → i32 ajustado por add_offset → elementos de Vec<i32> produzidos por collect(). O formatador :? exibe o vetor em um formato de depuração legível.
Salve e saia. Em seguida, verifique e execute:
cargo check
cargo run --quiet
A nova última linha deve ser:
Adjusted all: [14, 20, 1, 26, 32]
Até mesmo o -1 inválido foi transformado em 1; map transforma os itens, mas não decide quais itens devem permanecer.
Mantenha apenas as leituras válidas com filter
Nesta etapa, você usará filter para manter as leituras não negativas e deixar o vetor original inalterado.
O adaptador filter de um iterador chama uma closure predicada para cada candidato. Um predicado retorna true para manter um item ou false para rejeitá-lo. Abra o código-fonte:
nano src/main.rs
Substitua o comentário da Etapa 3 por:
let valid: Vec<i32> = readings
.iter()
.copied()
.filter(|reading| *reading >= 0)
.collect();
println!("Valid only: {valid:?}");
As cadeias de métodos podem continuar em linhas indentadas depois de um ponto. iter() pega o vetor por empréstimo, e copied() transforma cada item emprestado &i32 em um i32; isso é barato porque os inteiros implementam Copy. Em seguida, o predicado de filter recebe uma referência temporária a cada candidato i32, para poder inspecioná-lo sem consumi-lo; por isso a comparação usa *reading. O fluxo de tipos é &i32 → i32 copiado → i32 mantido → Vec<i32>.
Salve e saia. Em seguida, execute:
cargo run --quiet
A última linha deve ser:
Valid only: [12, 18, 24, 30]
O valor negativo não aparece, enquanto os valores mantidos continuam na ordem original.
Combine um pipeline completo de processamento
Nesta etapa, você encadeará a filtragem e o mapeamento, coletará as leituras processadas e somará esse resultado.
Os adaptadores de iterador são preguiçosos: filter e map descrevem o trabalho, mas os itens só passam por eles quando uma operação consumidora solicita resultados. collect e sum são operações consumidoras. Elas consomem o pipeline do iterador, não o vetor de origem emprestado.
Abra o código-fonte:
nano src/main.rs
Substitua o comentário da Etapa 4 por:
let valid_adjusted: Vec<i32> = readings
.iter()
.copied()
.filter(|reading| *reading >= 0)
.map(add_offset)
.collect();
let valid_total: i32 = valid_adjusted.iter().sum();
println!("Valid adjusted: {valid_adjusted:?}");
println!("Valid adjusted total: {valid_total}");
O pipeline primeiro rejeita -1. Depois, map(add_offset) passa cada i32 restante para a closure definida anteriormente. Você pode fornecer diretamente o nome de uma closure quando os tipos de parâmetro e de retorno forem compatíveis com o adaptador. O segundo iterador pega o vetor coletado por empréstimo e calcula a soma.
Salve e saia. Em seguida, verifique e execute:
cargo check
cargo run --quiet
As duas últimas linhas devem ser:
Valid adjusted: [14, 20, 26, 32]
Valid adjusted total: 92
A ordem das operações é importante: filtrar antes de mapear impede que a leitura inválida se transforme em um valor positivo aparentemente válido. O vetor original readings foi apenas emprestado e ainda poderia ser usado depois.
Resumo
Você comparou um loop explícito com um iterador, escreveu e reutilizou uma closure, transformou valores com map, selecionou valores com filter, coletou um novo vetor e calculou um total com sum. Você também preservou o vetor de origem iniciando cada pipeline com um iterador por empréstimo.


