Bibliothèques de gestion d'erreurs en Rust

thiserrorFournit une macro dérivée pratique pour les types d'erreur personnalisés
snafuCadre de gestion et de signalement d'erreurs avec contexte
anyhowBibliothèque flexible de gestion et de signalement d'erreurs

thiserror vs snafu

thiserror

thiserror est une bibliothèque légère qui fournit une macro dérivée pour simplifier la définition d'erreurs.

Caractéristiques :

  • Syntaxe concise, faible en formalités
  • Idéal pour créer des bibliothèques de types d'erreur et des API
  • Généralement utilisé pour créer des bibliothèques destinées à d'autres
use thiserror::Error;

#[derive(Error, Debug)]
pub enum DataError {
    #[error("Erreur de base de données : {0}")]
    DatabaseError(#[from] sqlx::Error),
    
    #[error("Erreur de validation : {0}")]
    ValidationError(String),
    
    #[error("Enregistrement introuvable")]
    NotFound,
}

snafu

snafu offre un cadre plus complet de gestion d'erreurs, axé sur le contexte et la chaîne d'erreurs.

Caractéristiques :

  • Encourage l'ajout précis de contexte d'erreur via le modèle "sélecteur de contexte"
  • Recommande un modèle "une énumération d'erreur par module"
  • Prend en charge les variantes d'erreur de style structure et tuple
  • Prise en charge intégrée de la rétroaction
use snafu::{Snafu, ResultExt, Backtrace};

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("Impossible de lire le fichier de configuration {filename:?}"))]
    ReadConfig {
        filename: String,
        source: std::io::Error,
        backtrace: Backtrace,
    },
    
    // Style tuple également possible
    #[snafu(display("Erreur IO"))]
    Io(#[snafu(source)] std::io::Error, #[snafu(backtrace)] Backtrace),
}

// Exemple de sélecteur de contexte
fn read_config(path: &str) -> Result<Config, Error> {
    std::fs::read_to_string(path).context(ReadConfigSnafu { filename: path })?;
    // ...
}

Comparaison

Caractéristiquethiserrorsnafu
Concision syntaxiquePlus concisePlus verbeuse
Contexte d'erreurSupport basiqueMécanisme de contexte riche
Échelle adaptéePetits à moyens projetsMoyens à grands projets
Lignes de code~2 lignes par erreur~5 lignes par erreur
Organisation des erreursGénéralement une seule énumération d'erreurEncourage les énumérations d'erreur par module
Prise en charge de la rétroactionNon intégréeIntégrée

Recommandation de choix:

  • Choisir thiserror pour des types d'erreur simples et clairs, surtout dans les bibliothèques
  • Choisir snafu pour une gestion d'erreurs plus structurée, notamment dans les grandes applications

anyhow

anyhow est une bibliothèque de gestion d'erreurs différente des précédentes, axée sur les applications plutôt que sur les bibliothèques.

Caractéristiques :

  • Conçue pour la gestion d'erreurs dans les applications, pas dans les bibliothèques
  • Fournit un type dynamique anyhow::Error pouvant contenir toute erreur implémentant le trait Error
  • Simplifie le traitement des erreurs à travers plusieurs types
  • Ne nécessite pas de définir des types d'erreur personnalisés
use anyhow::{Context, Result};

fn main() -> Result<()> {
    let config = std::fs::read_to_string("config.json")
        .context("Impossible de lire le fichier de configuration")?;
        
    let app_config: AppConfig = serde_json::from_str(&config)
        .context("Format de configuration invalide")?;
        
    // Utilise Result<T> comme alias de type pour Result<T, anyhow::Error>
    Ok(())
}

anyhow vs thiserror/snafu:

  • anyhow se concentre sur la gestion rapide des erreurs dans les applications
  • thiserror/snafu se concentrent sur la création de hiérarchies précises de types d'erreur
  • anyhow est généralement utilisé dans le code des applications
  • thiserror/snafu sont généralement utilisés dans le code des bibliothèques

En pratique, anyhow et thiserror sont souvent utilisés ensemble : les bibliothèques utilisent thiserror pour définir des types d'erreur précis, tandis que les applications utilisent anyhow pour gérer diverses sources d'erreur.