Bibliotecas de Tratamento de Erros em Rust

  • thiserror fornece macros derivadas convenientes para tipos de erro personalizados.

  • snafu é um framework de tratamento e relato de erros com contexto.

  • anyhow é uma biblioteca flexível de tratamento e relato de erros.

thiserror vs snafu

thiserror

thiserror é uma biblioteca leve que fornece macros derivadas para simplificar definições de erro.

Características:

  • Sintaxe concisa com baixa cerimônia
  • Adequado para criar bibliotecas e APIs de tipos de erro
  • Normalmente usado para criar bibliotecas destinadas a outros
use thiserror::Error;

#[derive(Error, Debug)]
pub enum DataError {
    #[error("Erro de banco de dados: {0}")]
    DatabaseError(#[from] sqlx::Error),
    
    #[error("Erro de validação: {0}")]
    ValidationError(String),
    
    #[error("Registro não encontrado")]
    NotFound,
}

snafu

snafu fornece um framework de tratamento de erros mais abrangente com foco em contexto de erro e encadeamento de erros.

Características:

  • Incentiva adição de contexto de erro mais preciso através do padrão "seletor de contexto"
  • Promove o padrão "um enum de erro por módulo"
  • Suporta variantes de erro em estilo struct e tupla
  • Suporte integrado a backtrace
use snafu::{Snafu, ResultExt, Backtrace};

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("Falha ao ler arquivo de configuração {filename:?}"))]
    ReadConfig {
        filename: String,
        source: std::io::Error,
        backtrace: Backtrace,
    },
    
    // Estilo tupla também é suportado
    #[snafu(display("Erro de IO"))]
    Io(#[snafu(source)] std::io::Error, #[snafu(backtrace)] Backtrace),
}

// Exemplo de seletor de contexto
fn read_config(path: &str) -> Result<Config, Error> {
    std::fs::read_to_string(path).context(ReadConfigSnafu { filename: path })?;
    // ...
}

Comparação

Característicathiserrorsnafu
Concisão sintáticaMais concisoMais verboso
Contexto de erroSuporte básicoMecanismos ricos de contexto
Escala adequadaPequenos a médios projetosMédios a grandes projetos
Linhas por erro~2 linhas por erro~5 linhas por erro
Organização de errosNormalmente enum único de erroIncentiva enums de erro por módulo
Suporte a backtraceSem suporte integradoSuporte integrado

Conselhos de seleção:

  • Escolha thiserror quando precisar de tipos de erro simples e claros, especialmente em bibliotecas
  • Escolha snafu quando precisar de tratamento de erro mais estruturado, particularmente em grandes aplicações

anyhow

anyhow é uma biblioteca de tratamento de erros diferente das duas anteriores, focando em aplicações em vez de bibliotecas.

Características:

  • Projetado para tratamento de erros em aplicações, não bibliotecas
  • Fornece um tipo dinâmico anyhow::Error que pode conter qualquer erro implementando a trait Error
  • Simplifica o tratamento entre múltiplos tipos de erro
  • Não requer definição de tipos de erro personalizados
use anyhow::{Context, Result};

fn main() -> Result<()> {
    let config = std::fs::read_to_string("config.json")
        .context("Falha ao ler arquivo de configuração")?;
        
    let app_config: AppConfig = serde_json::from_str(&config)
        .context("Formato de configuração inválido")?;
        
    // Usando Result<T> como alias de tipo para Result<T, anyhow::Error>
    Ok(())
}

anyhow vs thiserror/snafu:

  • anyhow foca no tratamento de erros durante desenvolvimento rápido de aplicações
  • thiserror/snafu focam na criação de hierarquias precisas de tipos de erro
  • anyhow é normalmente usado em código de aplicação
  • thiserror/snafu são normalmente usados em código de biblioteca

Na prática, anyhow e thiserror são frequentemente usados juntos: bibliotecas usam thiserror para definir tipos de erro precisos, enquanto aplicações usam anyhow para tratar erros de várias fontes.