Aprendendo Rust: Errors

Descubra como tratar exceções na linguagem

Em Rust, existem dois tipos de erros, os irrecuperáveis e os recuperáveis, vamos entender a diferença entre eles e entender qual contexto é pertinente ao uso.

Este post é um resumo traduzido dos conceitos que você pode encontrar no livro, que é gratuito: The Rust Programming Language

Erros irrecuperáveis

Já sabemos que rust tem por filosofia ser seguro e confiável, caminhando ao favor disso obviamente temos restrições de seguranças para permitir que não existam problemas na sua runtime, caso um problema apareça a linguagem invoca um macro chamado panic, que por padrão retorna o erro que causou aquela invocação.

Você pode invocar a macro panic quando achar necessário dentro do seu código,

fn main() {
    panic!("Erro de bios");
}

Além do código acima, um possível uso de erro irrecuperável é quando você decide buscar ou verificar um valor que pode ser nulo mas seu programa não funciona se esse valor não forr resolvido, para isso temos unwrap e expect que contribuem para uma forma mais clara de dizer "tente resolver isso, se não resolver panic!

Erros recuperáveis

Como a maioria dos programas são feitos para ter iteração humana, erros são esperados e seria impossível desenvolver algo que resulte em panic constantemente.

Por isso o Result implementa o enum genérico que resolve o valor caso o tipo T exista ou retorne o erro E caso tenha dado algum erro.

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

Como normalmente lidamos com enums em Rust, esse Result é avaliado por um match, veja no exemplo abaixo

use std::fs::File;

fn main() {
    let can_be_file = File::open("data.txt");

    let result = match can_be_file {
        Ok(resolved_file) => resolved_file,
        Err(error) => println!("Failed: {:?}", error),
    };
}

Nesse caso, eu procuro por um arquivo, caso ache o arquivo ele retorna o valor de T e caso não ache ele passa um E para que eu trate da maneira que eu quiser, neste caso apenas um println!.

Erros são bem imprevisíveis mas sabemos que para cada método utilizado temos alguns retornos de erro possíveis, e esses podem ser consultados com kind dentro do tipo E.

use std::fs::File;

fn main() {
    let can_be_file = File::open("data.txt");

    let result = match can_be_file {
        Ok(resolved_file) => resolved_file,
        Err(error) => match error.kind() {
          GenericKindError::NotFound => println!("404"),
        };
    }
}

O operador ?

Um facilitador enorme para lidar com valores que podem ser resolvidos ou falhar é o operador ? ele funciona basicamente com o conceito de Propagating Errors ****que consiste em reconhecer que funções podem falhar e basear o tipo de retorno dessa função para retornar seu erro para quem a chamou.

use std::fs::File;
use std::io::{self, Read};

fn find() -> Result<String, io::Error> {
    let can_be_file = File::open("data.txt")?;
}

Um código curto e que substitui completamente a necessidade de um match para apenas retornar T ou E.

Quando usar panic ou não

Nosso pensamento ao lidar com o panic do Rust é evita-lo ao máximo para manter nossa aplicação rodando, entretanto, existem cenários onde é uma melhor prática deixar o programa quebrar do que continuar rodando com Result .

Testes ou protótipos de código

Em casos de testes ou protótipos de código, determinar um controle de erro apurado pode dificultar muito sua demonstração ou comparação de ser entendida, para isso se torna bem mais intuitivo deixar que o programa cause panic,

Dois métodos que podem ser ótimos para esses casos são unwrap e o expect pois tem a clareza de que o programa irá tentar resolver aquele valor mas vão automaticamente disparar um panic caso aconteça algo de errado.

Estado ruim

Se a sua aplicação falha em alguma validação de comparação ou contrato, por exemplo, ela provavelmente está em um bad state, nesses casos é extremamente recomendado que você não permita que sua aplicação continue funcionando porque você está comprometendo completamente a segurança da aplicação, deixe o panic agir.

Em qualquer outro cenário de erro

Seja input externo ou valor possivelmente nulo você deve tratar com Result<T, E> sem sombra de dúvidas.

Esse foi o ultimo resumo técnico sobre Rust, me diverti muito aprendendo a linguagem mas ainda preciso melhorar muito como profissional na minha área para explorar novos horizontes. Por hoje é isso pessoal, vejo vocês na próxima. 😁