Rust Error Handling Libraries

  • thiserror provides convenient derive macros for custom error types.

  • snafu is an error handling and reporting framework with context.

  • anyhow is a flexible error handling and reporting library.

thiserror vs snafu

thiserror

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

Features:

  • Concise syntax with low ceremony
  • Suitable for creating error type libraries and APIs
  • Typically used for creating libraries intended 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 provides a more comprehensive error handling framework with a focus on error context and error chaining.

Features:

  • Encourages more precise error context addition through the "context selector" pattern
  • Promotes the "one error enum per module" pattern
  • 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 is 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 concisenessMore conciseMore verbose
Error contextBasic supportRich context mechanisms
Suitable scaleSmall to medium projectsMedium to large projects
Lines per error~2 lines per error~5 lines per error
Error organizationUsually single error enumEncourages module-level error enums
Backtrace supportNo built-in supportBuilt-in support

Selection advice:

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

anyhow

anyhow is a different error handling library from the above two, focusing on applications rather than libraries.

Features:

  • Designed for error handling in applications, not libraries
  • Provides a dynamic anyhow::Error type that can contain any error implementing the 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 a type alias for Result<T, anyhow::Error>
    Ok(())
}

anyhow vs thiserror/snafu:

  • anyhow focuses on error handling during rapid 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 use thiserror to define precise error types, while applications use anyhow to handle errors from various sources.