Librerie per la Gestione degli Errori in Rust

  • thiserror fornisce macro derive convenienti per tipi di errore personalizzati.

  • snafu è un framework per la gestione e segnalazione degli errori con contesto.

  • anyhow è una libreria flessibile per la gestione e segnalazione degli errori.

thiserror vs snafu

thiserror

thiserror è una libreria leggera che fornisce macro derive per semplificare le definizioni degli errori.

Caratteristiche:

  • Sintassi concisa con poca cerimonia
  • Adatta per creare librerie di tipi di errore e API
  • Tipicamente utilizzata per creare librerie destinate ad altri
use thiserror::Error;

#[derive(Error, Debug)]
pub enum DataError {
    #[error("Errore database: {0}")]
    DatabaseError(#[from] sqlx::Error),
    
    #[error("Errore validazione: {0}")]
    ValidationError(String),
    
    #[error("Record non trovato")]
    NotFound,
}

snafu

snafu fornisce un framework di gestione errori più completo con focus sul contesto e concatenamento degli errori.

Caratteristiche:

  • Promuove l'aggiunta di contesto più preciso attraverso il pattern "selettore di contesto"
  • Incoraggia il pattern "un enum di errore per modulo"
  • Supporta sia varianti di errore struct che tuple
  • Supporto integrato per backtrace
use snafu::{Snafu, ResultExt, Backtrace};

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("Impossibile leggere file di configurazione {filename:?}"))]
    ReadConfig {
        filename: String,
        source: std::io::Error,
        backtrace: Backtrace,
    },
    
    // Anche lo stile tuple è supportato
    #[snafu(display("Errore IO"))]
    Io(#[snafu(source)] std::io::Error, #[snafu(backtrace)] Backtrace),
}

// Esempio selettore di contesto
fn read_config(path: &str) -> Result<Config, Error> {
    std::fs::read_to_string(path).context(ReadConfigSnafu { filename: path })?;
    // ...
}

Confronto

Caratteristicathiserrorsnafu
Conciseness sintatticaPiù concisoPiù verboso
Contesto erroreSupporto baseMeccanismi di contesto ricchi
Scala adattaProgetti piccoli/mediProgetti medi/grandi
Righe per errore~2 righe per errore~5 righe per errore
Organizzazione erroriSolitamente enum singoloIncoraggia enum per modulo
Supporto backtraceNessun supporto integratoSupporto integrato

Consigli di scelta:

  • Scegli thiserror quando hai bisogno di tipi di errore semplici e chiari, specialmente nelle librerie
  • Scegli snafu quando hai bisogno di gestione errori più strutturata, particolarmente in applicazioni grandi

anyhow

anyhow è una libreria di gestione errori diversa dalle due precedenti, focalizzata sulle applicazioni piuttosto che sulle librerie.

Caratteristiche:

  • Progettata per la gestione errori nelle applicazioni, non nelle librerie
  • Fornisce un tipo dinamico anyhow::Error che può contenere qualsiasi errore che implementi il tratto Error
  • Semplifica la gestione tra più tipi di errore
  • Non richiede la definizione di tipi di errore personalizzati
use anyhow::{Context, Result};

fn main() -> Result<()> {
    let config = std::fs::read_to_string("config.json")
        .context("Impossibile leggere file di configurazione")?;
        
    let app_config: AppConfig = serde_json::from_str(&config)
        .context("Formato configurazione non valido")?;
        
    // Utilizzo di Result<T> come alias di tipo per Result<T, anyhow::Error>
    Ok(())
}

anyhow vs thiserror/snafu:

  • anyhow si concentra sulla gestione errori durante lo sviluppo rapido di applicazioni
  • thiserror/snafu si concentrano sulla creazione di gerarchie precise di tipi di errore
  • anyhow è tipicamente utilizzato nel codice applicativo
  • thiserror/snafu sono tipicamente utilizzati nel codice di libreria

Nella pratica, anyhow e thiserror sono spesso usati insieme: le librerie usano thiserror per definire tipi di errore precisi, mentre le applicazioni usano anyhow per gestire errori da varie fonti.