Rust Admin

Guia Completo de Result<T, E> em Rust: Tratamento de Erros sem Exceções Já leu

O Tipo Result em Rust Result é o tipo fundamental de Rust para tratamento de erros, diferente completamente do modelo de exceções. Ele é um enum que representa dois estados: sucesso ( ) ou falha ( ). A principal vantagem é forçar o programador a lidar com possibilidades de erro em tempo de compilação, eliminando a surpresa de exceções não tratadas que vemos em linguagens como Java ou Python. Todo Result deve ser manipulado explicitamente. Ignorar um Result gera um aviso do compilador, garantindo que você nunca "deixe passar" um erro involuntariamente. Isso torna o código mais robusto e previsível desde o início do desenvolvimento. Padrões de Tratamento Básicos Match e Pattern Matching O é a forma mais explícita de lidar com Result. Você obriga o código a considerar ambos os casos: O é verbose mas extremamente claro. Cada branch deve retornar o mesmo tipo, o que incentiva tratamento coeso de erros. Operador (Propagação) O ponto de interrogação é açúcar

O Tipo Result em Rust

Result é o tipo fundamental de Rust para tratamento de erros, diferente completamente do modelo de exceções. Ele é um enum que representa dois estados: sucesso (Ok(T)) ou falha (Err(E)). A principal vantagem é forçar o programador a lidar com possibilidades de erro em tempo de compilação, eliminando a surpresa de exceções não tratadas que vemos em linguagens como Java ou Python.

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

Todo Result deve ser manipulado explicitamente. Ignorar um Result gera um aviso do compilador, garantindo que você nunca "deixe passar" um erro involuntariamente. Isso torna o código mais robusto e previsível desde o início do desenvolvimento.

Padrões de Tratamento Básicos

Match e Pattern Matching

O match é a forma mais explícita de lidar com Result. Você obriga o código a considerar ambos os casos:

use std::fs::File;

fn ler_arquivo(caminho: &str) -> Result<String, std::io::Error> {
    let mut arquivo = File::open(caminho)?;
    let mut conteudo = String::new();
    arquivo.read_to_string(&mut conteudo)?;
    Ok(conteudo)
}

fn main() {
    match ler_arquivo("dados.txt") {
        Ok(conteudo) => println!("Lido: {}", conteudo),
        Err(erro) => eprintln!("Erro: {}", erro),
    }
}

O match é verbose mas extremamente claro. Cada branch deve retornar o mesmo tipo, o que incentiva tratamento coeso de erros.

Operador ? (Propagação)

O ponto de interrogação é açúcar sintático que propaga erros automaticamente. Se uma operação falha, o erro é retornado imediatamente da função atual:

fn processar_dados() -> Result<i32, Box<dyn std::error::Error>> {
    let arquivo = File::open("dados.txt")?;
    let numero: i32 = std::fs::read_to_string("numero.txt")?
        .trim()
        .parse()?;
    Ok(numero * 2)
}

O ? só funciona em funções que retornam Result. Para main(), use .unwrap_or_else() ou retorne Result do main (Rust 1.26+).

Métodos Essenciais de Result

Result fornece métodos combinadores que permitem transformar, filtrar e processar valores sem destruir a estrutura do tipo:

fn main() {
    let resultado: Result<i32, String> = Ok(5);

    // map: transformar o valor Ok
    let dobrado = resultado.map(|x| x * 2); // Ok(10)

    // map_err: transformar o erro
    let erro_msg = Err::<i32, &str>("falha").map_err(|e| format!("Erro: {}", e));

    // unwrap_or: valor padrão em caso de erro
    let valor = Err::<i32, String>("problema".to_string()).unwrap_or(0); // 0

    // and_then: acorrentar operações que retornam Result
    let resultado = Ok(10)
        .and_then(|x| if x > 5 { Ok(x * 2) } else { Err("muito pequeno") });

    // or_else: oferecer alternativa em caso de erro
    let fallback = Err::<i32, &str>("erro1")
        .or_else(|_| Ok::<i32, &str>(99)); // Ok(99)
}

Esses combinadores eliminam a necessidade de múltiplos match aninhados, tornando o código funcional e elegante.

Erros Customizados e Boas Práticas

Definindo Tipos de Erro Próprios

Para aplicações reais, crie tipos de erro específicos que implementem std::error::Error:

use std::error::Error;
use std::fmt;

#[derive(Debug)]
enum ErroArquivo {
    NaoEncontrado(String),
    PermissaoNegada,
    FormatoInvalido(String),
}

impl fmt::Display for ErroArquivo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ErroArquivo::NaoEncontrado(nome) => 
                write!(f, "Arquivo não encontrado: {}", nome),
            ErroArquivo::PermissaoNegada => 
                write!(f, "Permissão negada ao acessar arquivo"),
            ErroArquivo::FormatoInvalido(motivo) => 
                write!(f, "Formato inválido: {}", motivo),
        }
    }
}

impl Error for ErroArquivo {}

fn validar_json(conteudo: &str) -> Result<(), ErroArquivo> {
    if conteudo.is_empty() {
        return Err(ErroArquivo::FormatoInvalido("JSON vazio".into()));
    }
    Ok(())
}

Tipos de erro customizados permitem que quem chama sua função entenda exatamente quais falhas são possíveis e trate cada uma apropriadamente.

Crate anyhow para Contexto

Em projetos maiores, use a crate anyhow para adicionar contexto aos erros sem criar tipos complexos:

// Adicione ao Cargo.toml: anyhow = "1.0"
use anyhow::{Context, Result};

fn ler_config() -> Result<String> {
    let conteudo = std::fs::read_to_string("config.toml")
        .context("Falha ao ler arquivo de configuração")?;
    Ok(conteudo)
}

fn main() {
    match ler_config() {
        Ok(config) => println!("Config carregada"),
        Err(e) => eprintln!("Erro: {:?}", e),
    }
}

O anyhow::Result é equivalente a Result<T, Box<dyn Error>>, oferecendo flexibilidade com contexto.

Conclusão

Result não é apenas um tipo — é uma filosofia de design que força você a ser explícito com erros. Os três aprendizados principais são: (1) todo erro deve ser tratado ou propagado conscientemente; (2) combinadores como map, and_then e ? tornam o tratamento elegante e funcional; (3) erros customizados e crates como anyhow escalam bem em projetos reais.

Domine esses padrões e você escreverá código Rust robusto, previsível e mantível.

Referências


Artigos relacionados