Iteração em Rust: como parar ao encontrar um erro e retornar Result::Err

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

Introdução: por que o iterador não quer parar?

\n\n

Imagine: você está escrevendo um programa em Rust que processa uma lista de números — analisa strings, lê arquivos, realiza algumas transformações. Você usa Iterator::map para percorrer cada elemento. E então — puf! — um dos elementos se transforma em um erro Result::Err. Seria lógico parar imediatamente a iteração e retornar esse erro para cima. Mas o map simplesmente continua funcionando, como se nada tivesse acontecido. Por quê? E o principal — como corrigir isso?

\n\n

Esta pergunta é uma das mais populares no Stack Overflow entre iniciantes em Rust. E não é à toa. Os mecanismos de tratamento de erros em Rust são flexíveis, mas exigem uma abordagem consciente. Neste artigo, vamos analisar o problema de ponta a ponta, considerar várias soluções e escolher a mais elegante. Vamos lá!

\n\n

Problema: map não para

\n\n

Vamos começar com um exemplo clássico. Suponha que temos um vetor de strings que queremos converter em números:

\n\n
fn main() {\n    let inputs = vec!["42", "hello", "100"];\n\n    let result: Vec<Result<i32, std::num::ParseIntError>> =\n        inputs.iter().map(|s| s.parse::<i32>()).collect();\n\n    println!("{:?}", result);\n}
\n\n

Este código será compilado e executado. Mas o que obteremos? Um vetor de Results: [Ok(42), Err(ParseIntError), Ok(100)]. A iteração não parou em "hello", mas continuou. Para muitas tarefas, isso é normal — você quer coletar todos os erros. Mas e se você precisar da primeira exceção? Por exemplo, ao carregar uma configuração: se um parâmetro estiver incorreto, não adianta verificar os demais.

\n\n

O problema é que map é uma transformação preguiçosa. Ele não sabe o que é "parar". Ele apenas diz: "Me dê um elemento, aplicarei a função, retornarei o resultado". Todo o controle de pausa e continuação está no consumidor do iterador (collect, for, fold, etc.). E o collect para Vec simplesmente vai até o final.

\n\n

Solução 1: collect com FromIterator (a mais popular)

\n\n

A resposta mais comum no Stack Overflow é usar collect::<Result<Vec<_>, _>>(). Rust sabe que é possível coletar um iterador de Results em um único Result com um vetor dentro. Se encontrar um Err, a coleta é interrompida e esse erro é retornado.

\n\n
fn main() -> Result<(), std::num::ParseIntError> {\n    let inputs = vec!["42", "hello", "100"];\n\n    let result: Vec<i32> =\n        inputs.iter()\n              .map(|s| s.parse::<i32>())\n              .collect::<Result<Vec<_>, _>>()?; // <-- mágica!\n\n    println!("{:?}", result);\n    Ok(())\n}
\n\n

Como funciona? O tipo Result<Vec<i32>, ParseIntError> implementa a trait FromIterator. Quando o collect encontra um Err, ele o retorna imediatamente, sem processar os elementos restantes. Se tudo estiver bem — você obtém Ok(vetor). É curto, idiomático e eficiente.

\n\n

Desvantagem: você perde a informação sobre em qual elemento ocorreu o erro (a menos que o envolva em seu próprio tipo). Mas para scripts rápidos, é ideal.

\n\n

Solução 2: filter_map para ignorar erros

\n\n

Às vezes, você não quer parar, mas apenas ignorar os elementos com falha. Por exemplo, contar apenas strings válidas. Para isso, o filter_map é perfeito:

\n\n
fn main() {\n    let inputs = vec!["42", "hello", "100", "abc", "7"];\n\n    let numbers: Vec<i32> = inputs.iter()\n        .filter_map(|s| s.parse::<i32>().ok()) // .ok() transforma Err em None\n        .collect();\n\n    println!("{:?}", numbers); // [42, 100, 7]\n}
\n\n

filter_map recebe

Blogs

Book Recommendations