Introdução
Algumas falhas são bugs de programação, mas outras são esperadas em tempo de execução: uma pessoa pode inserir um texto malformado ou um valor fora do intervalo permitido. O Rust representa esses resultados recuperáveis com Result<T, E>, que pode ser Ok(T) ou Err(E).
Você analisará textos válidos e inválidos sem causar um panic, retornará erros de validação úteis e usará o operador ? para encaminhar falhas ao chamador. O projeto Cargo preparado mantém cada alteração concentrada no tratamento de erros.
Faça a correspondência de um Result bem-sucedido
Nesta etapa, você analisará um texto como inteiro e fará a correspondência do resultado bem-sucedido.
O projeto está em /home/labex/project/result-workshop, e src/main.rs contém quatro trechos reservados para esta prática. Entre no diretório do projeto e abra o código-fonte:
cd /home/labex/project/result-workshop
nano src/main.rs
Substitua o comentário da Etapa 1 por:
let valid = "42".parse::<i32>();
match valid {
Ok(value) => println!("Parsed value: {value}"),
Err(error) => println!("Parse error: {error}"),
}
parse é um método reutilizável que pode produzir diferentes tipos de saída. A parte ::<i32> informa o tipo de saída desta chamada e pede ao Rust que interprete a string como um inteiro com sinal de 32 bits. Esse argumento de tipo entre sinais de menor e maior é diferente de um caminho como Message::Text, embora ambos usem ::. A análise pode falhar, portanto o tipo retornado é um Result. Ok(value) contém o inteiro analisado; Err(error) contém informações sobre o motivo da falha. Um match precisa tratar as duas variantes.
Salve com Ctrl+O, pressione Enter e saia com Ctrl+X. Verifique e execute:
cargo check
cargo run --quiet
Parsed value: 42
Somente o braço Ok foi executado, porque "42" é um texto válido para um inteiro.
Recupere-se de uma entrada inválida
Nesta etapa, você tratará um texto malformado como dado, em vez de permitir que ele encerre o programa.
Um panic indica que o programa alcançou um estado que seu projeto não esperava e normalmente interrompe a execução. Uma entrada externa malformada é esperada, portanto deve permanecer como um Err que o programa pode analisar e informar. Abra o código-fonte:
nano src/main.rs
Substitua o comentário da Etapa 2 por:
let invalid = "many".parse::<i32>();
match invalid {
Ok(value) => println!("Unexpected value: {value}"),
Err(error) => println!("Handled invalid input: {error}"),
}
Esta correspondência ainda cobre os dois resultados possíveis, mas o texto preparado seleciona o braço Err. Salve e saia. Em seguida, execute:
cargo run --quiet
A segunda linha deve ser:
Handled invalid input: invalid digit found in string
O programa continuou normalmente e transformou o erro do analisador em uma mensagem útil. Evite usar unwrap() com dados externos: nesse mesmo Err, unwrap() causaria um panic em vez de permitir a recuperação.
Analise e valide usando o operador de interrogação
Nesta etapa, você criará uma função que pode retornar uma porcentagem ou uma mensagem de erro útil.
O tipo Result<u32, String> significa que o sucesso contém um inteiro sem sinal e a falha contém um texto de erro pertencente ao valor. O operador ? obtém o valor de Ok; se encontrar Err, ele retorna imediatamente esse erro da função atual.
Abra o código-fonte:
nano src/main.rs
Adicione esta função acima de main:
fn parse_percentage(text: &str) -> Result<u32, String> {
let value = text
.parse::<u32>()
.map_err(|_| format!("not a whole number: {text}"))?;
if value <= 100 {
Ok(value)
} else {
Err(format!("outside 0..=100: {value}"))
}
}
Leia a função como uma sequência curta. Primeiro, parse::<u32>() tenta transformar o texto em um número inteiro. Em seguida, map_err(...) altera somente uma falha de análise, transformando-a em uma String mais clara que contém a entrada original.
Depois, ? obtém o número em caso de sucesso ou retorna imediatamente esse novo erro de parse_percentage. Se a análise for bem-sucedida, o if valida o número e retorna Ok(value) ou um Err indicando que o valor está fora do intervalo.
O parâmetro _ da closure significa que o valor original do erro do analisador é ignorado deliberadamente, porque a nova mensagem o substitui. Aqui, _ é um parâmetro ignorado; antes, _ => era um braço curinga de match, e let _ = descartava um resultado completo.
Substitua o comentário da Etapa 3 dentro de main por:
println!("85 => {:?}", parse_percentage("85"));
println!("150 => {:?}", parse_percentage("150"));
println!("many => {:?}", parse_percentage("many"));
O formatador :? mostra a variante Ok ou Err neste experimento de aprendizagem. Salve e saia. Em seguida, verifique e execute:
cargo check
cargo run --quiet
As novas linhas devem ser:
85 => Ok(85)
150 => Err("outside 0..=100: 150")
many => Err("not a whole number: many")
Agora a função distingue um valor válido, um valor analisado mas fora do intervalo e um texto malformado.
Propague um erro por outra função
Nesta etapa, você usará ? em um segundo limite de função e tratará o resultado final em main.
Uma função auxiliar nem sempre sabe como a aplicação deve exibir ou recuperar-se de um erro. Ela pode propagar o Err para cima, deixando a decisão para o chamador. Abra o código-fonte:
nano src/main.rs
Adicione esta função entre parse_percentage e main:
fn acceptance_message(text: &str) -> Result<String, String> {
let value = parse_percentage(text)?;
Ok(format!("accepted {value}%"))
}
Se a análise ou a validação falhar, ? retorna imediatamente a String de erro existente. Em caso de sucesso, a função envolve sua mensagem em Ok.
Substitua o comentário da Etapa 4 dentro de main por:
for text in ["73", "bad"] {
match acceptance_message(text) {
Ok(message) => println!("{text} => {message}"),
Err(message) => println!("{text} => error: {message}"),
}
}
O loop fornece uma entrada bem-sucedida e outra com falha. main é o limite que transforma cada resultado em uma saída exibida ao usuário. Salve e saia. Em seguida, verifique e execute:
cargo check
cargo run --quiet
As linhas finais devem ser:
73 => accepted 73%
bad => error: not a whole number: bad
O mesmo contexto de erro criado por parse_percentage foi preservado durante a propagação por acceptance_message.
Resumo
Você fez a correspondência de valores Result bem-sucedidos e malsucedidos, tratou entradas malformadas como uma condição recuperável, criou erros String com contexto usando map_err e propagou falhas pelos limites das funções com ?, em vez de causar um panic com unwrap.


