Introdução
Neste laboratório, exploramos diferentes estratégias para lidar com a possibilidade de falhas ao iterar sobre uma coleção de Results em Rust.
Nota: Se o laboratório não especificar um nome de arquivo, você pode usar qualquer nome de arquivo que desejar. Por exemplo, você pode usar
main.rs, compilar e executá-lo comrustc main.rs && ./main.
Iterando sobre Results
Uma operação Iter::map pode falhar, por exemplo:
fn main() {
let strings = vec!["tofu", "93", "18"];
let numbers: Vec<_> = strings
.into_iter()
.map(|s| s.parse::<i32>())
.collect();
println!("Results: {:?}", numbers);
}
Vamos analisar as estratégias para lidar com isso.
Ignorar os itens com falha com filter_map()
filter_map chama uma função e filtra os resultados que são None.
fn main() {
let strings = vec!["tofu", "93", "18"];
let numbers: Vec<_> = strings
.into_iter()
.filter_map(|s| s.parse::<i32>().ok())
.collect();
println!("Results: {:?}", numbers);
}
Coletar os itens com falha com map_err() e filter_map()
map_err chama uma função com o erro, então, adicionando isso à solução filter_map anterior, podemos salvá-los de lado durante a iteração.
fn main() {
let strings = vec!["42", "tofu", "93", "999", "18"];
let mut errors = vec![];
let numbers: Vec<_> = strings
.into_iter()
.map(|s| s.parse::<u8>())
.filter_map(|r| r.map_err(|e| errors.push(e)).ok())
.collect();
println!("Numbers: {:?}", numbers);
println!("Errors: {:?}", errors);
}
Falhar em toda a operação com collect()
Result implementa FromIterator para que um vetor de resultados (Vec<Result<T, E>>) possa ser transformado em um resultado com um vetor (Result<Vec<T>, E>). Assim que um Result::Err é encontrado, a iteração será encerrada.
fn main() {
let strings = vec!["tofu", "93", "18"];
let numbers: Result<Vec<_>, _> = strings
.into_iter()
.map(|s| s.parse::<i32>())
.collect();
println!("Results: {:?}", numbers);
}
Esta mesma técnica pode ser usada com Option.
Coletar todos os valores válidos e falhas com partition()
fn main() {
let strings = vec!["tofu", "93", "18"];
let (numbers, errors): (Vec<_>, Vec<_>) = strings
.into_iter()
.map(|s| s.parse::<i32>())
.partition(Result::is_ok);
println!("Numbers: {:?}", numbers);
println!("Errors: {:?}", errors);
}
Ao olhar para os resultados, você notará que tudo ainda está envolvido em Result. Um pouco mais de código boilerplate é necessário para isso.
fn main() {
let strings = vec!["tofu", "93", "18"];
let (numbers, errors): (Vec<_>, Vec<_>) = strings
.into_iter()
.map(|s| s.parse::<i32>())
.partition(Result::is_ok);
let numbers: Vec<_> = numbers.into_iter().map(Result::unwrap).collect();
let errors: Vec<_> = errors.into_iter().map(Result::unwrap_err).collect();
println!("Numbers: {:?}", numbers);
println!("Errors: {:?}", errors);
}
Resumo
Parabéns! Você concluiu o laboratório Iterating Over Results. Você pode praticar mais laboratórios no LabEx para aprimorar suas habilidades.