Pular para o conteúdo principal
Módulo 7 · Lição 2 de 214/14 no curso~15 min
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:

Rust
enum Result<T, E> {
    Ok(T),
    Err(E),
}

Podemos usar a correspondência de padrões para inspecionar o resultado:

Rust
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(...).

Rust
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 (normalmente Result, Option ou tipos que implementam FromResidual).


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:

Rust
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

Exercício#rust.m7.l2.e1
Tentativas: 0Carregando…

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.

Carregando editor…
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

Exercício#rust.m7.l2.e2
Tentativas: 0Carregando…

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.

Carregando editor…
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

Exercício#rust.m7.l2.e3
Tentativas: 0Carregando…

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.

Carregando editor…
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