Lições do módulo (2/2)
Tratamento de erros e o ? Operador
A filosofia de Rust incentiva você a reconhecer explicitamente a possibilidade de erros e estruturar seu código para lidar com eles antes que o programa seja compilado.
Os erros no Rust são divididos em duas categorias principais: erros irrecuperáveis (que causam encerramento imediato por meio da macro panic!) e erros recuperáveis (gerenciados usando o tipo Result<T, E>).
O resultado<T, E> correspondência de tipo e padrão
A maioria dos erros recuperáveis retorna um tipo Result<T, E>, que é uma enumeração definida como:
enum Result<T, E> {
Ok(T),
Err(E),
}Podemos usar a correspondência de padrões para inspecionar o resultado:
use std::fs::File;
fn main() {
let greeting_file_result = File::open("hello.txt");
let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => panic!("Problem opening the file: {:?}", error),
};
}Propagação de erros com o operador ?
Ao implementar uma função, em vez de tratar o erro diretamente dentro dela, geralmente queremos retornar o erro ao chamador para que ele possa decidir o que fazer. Este processo é chamado de propagação de erro.
Rust fornece o operador ? como um atalho sintático para propagar erros. Se o valor de Result for Ok, o valor interno de Ok será retornado pela expressão; se for Err, o erro Err é retornado de toda a função atual como se tivéssemos usado um return Err(...).
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {
let mut username_file = File::open("username.txt")?;
let mut username = String::new();
username_file.read_to_string(&mut username)?;
Ok(username)
}[!IMPORTANTE] O operador
?só pode ser usado em funções que retornam um tipo compatível com o valor ao qual é aplicado (normalmenteResult,Optionou tipos que implementamFromResidual).
Erros personalizados
Podemos definir nossos próprios tipos de erro personalizados implementando a característica std::fmt::Display (e opcionalmente std::error::Error) para fornecer uma representação legível do erro:
use std::fmt;
#[derive(Debug)]
struct MyError {
details: String,
}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Error: {}", self.details)
}
}
impl std::error::Error for MyError {}Experimente você mesmo
Exercício 1: Lendo um arquivo com o ? operador
Conclua a função read_username_from_file para que ela use o ? operador para propagar erros gerados ao abrir o arquivo e ler seu conteúdo na string de nome de usuário.
Mostrar dica
Adicione o caracter `?` após`File::open(...)`e após`username_file.read_to_string(...)` para propagar erros.
Solução disponível após 3 tentativas
Exercício 2: analisar e duplicar um número
Escreva uma função chamada parse_and_double que aceita uma referência de string &str e retorna um Result<i32, std::num::ParseIntError>. A função deve analisar a string usando val.parse::<i32>() com ? para propagar erros de análise e, em caso de sucesso, retornar o valor duplicado envolvido em Ok.
Mostrar dica
Use `val.parse::<i32>()?`para analisar a string e propagar o erro, então retorne`Ok(num \* 2)`.
Solução disponível após 3 tentativas
Exercício 3: Definindo um erro personalizado
Defina uma estrutura vazia chamada CustomError. Implemente a característica std::fmt::Display escrevendo 'Si e verificato un errore personalizzato' como a mensagem de erro no método fmt.
Mostrar dica
Declare `struct CustomError;`e implemente Display escrevendo`write!(f, "Se você verificar um erro personalizado")`dentro do método`fmt`.
Solução disponível após 3 tentativas