Rust Error Handling Libraries

thiserrorConvenient derive macros for custom error types
snafuError handling and reporting framework with context
anyhowFlexible error handling and reporting library

thiserror vs snafu

thiserror

thiserror is a lightweight library that provides derive macros to simplify error definition.

Features:

  • Concise syntax with low ceremony
  • Ideal for creating error type libraries and APIs
  • Typically used for creating libraries meant for others
use thiserror::Error;

#[derive(Error, Debug)]
pub enum DataError {
    #[error("Database error: {0}")]
    DatabaseError(#[from] sqlx::Error),
    
    #[error("Validation error: {0}")]
    ValidationError(String),
    
    #[error("Record not found")]
    NotFound,
}

snafu

snafu offers a more comprehensive error handling framework with emphasis on error context and chaining.

Features:

  • Encourages precise error context through "context selector" pattern
  • Promotes "one error enum per module" approach
  • Supports both struct and tuple-style error variants
  • Built-in backtrace support
use snafu::{Snafu, ResultExt, Backtrace};

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("Failed to read config file {filename:?}"))]
    ReadConfig {
        filename: String,
        source: std::io::Error,
        backtrace: Backtrace,
    },
    
    // Tuple-style also supported
    #[snafu(display("IO error"))]
    Io(#[snafu(source)] std::io::Error, #[snafu(backtrace)] Backtrace),
}

// Context selector example
fn read_config(path: &str) -> Result<Config, Error> {
    std::fs::read_to_string(path).context(ReadConfigSnafu { filename: path })?;
    // ...
}

Comparison

Featurethiserrorsnafu
Syntax simplicityMore conciseMore verbose
Error contextBasic supportRich context mechanism
Project scaleSmall to mediumMedium to large
Code lines~2 per error~5 per error
Error organizationTypically single enumEncourages module-level enums
Backtrace supportNo built-inBuilt-in

Recommendations:

  • Choose thiserror when you need straightforward error types, especially in libraries
  • Choose snafu when you need more structured error handling, particularly in large applications

anyhow

anyhow takes a different approach from the above libraries, focusing on applications rather than libraries.

Features:

  • Designed for application error handling, not libraries
  • Provides dynamic anyhow::Error type that can wrap any error implementing Error trait
  • Simplifies handling across multiple error types
  • No need to define custom error types
use anyhow::{Context, Result};

fn main() -> Result<()> {
    let config = std::fs::read_to_string("config.json")
        .context("Failed to read config file")?;
        
    let app_config: AppConfig = serde_json::from_str(&config)
        .context("Invalid config format")?;
        
    // Using Result<T> as type alias for Result<T, anyhow::Error>
    Ok(())
}

anyhow vs thiserror/snafu:

  • anyhow focuses on rapid error handling during application development
  • thiserror/snafu focus on creating precise error type hierarchies
  • anyhow is typically used in application code
  • thiserror/snafu are typically used in library code

In practice, anyhow and thiserror are often used together: libraries define precise error types with thiserror, while applications use anyhow to handle various error sources.