Introdução: por que o iterador não quer parar?
\n\nImagine: 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?
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\nProblema: map não para
\n\nVamos começar com um exemplo clássico. Suponha que temos um vetor de strings que queremos converter em números:
\n\nfn 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\nEste 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.
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.
Solução 1: collect com FromIterator (a mais popular)
\n\nA 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.
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\nComo 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.
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\nSoluçã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:
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\nfilter_map recebe