Rust エラーハンドリングライブラリ

thiserrorカスタムエラータイプのための便利な派生マクロを提供
snafuコンテキスト付きのエラーハンドリングとレポーティングフレームワーク
anyhow柔軟なエラーハンドリングとレポーティングライブラリ

thiserror vs snafu

thiserror

thiserrorは軽量なライブラリで、エラー定義を簡単にする派生マクロを提供します。

特徴:

  • 簡潔な構文、低儀式性
  • エラータイプライブラリやAPIの作成に適している
  • 主に他者が使用するライブラリの作成に用いられる
use thiserror::Error;

#[derive(Error, Debug)]
pub enum DataError {
    #[error("データベースエラー: {0}")]
    DatabaseError(#[from] sqlx::Error),
    
    #[error("バリデーションエラー: {0}")]
    ValidationError(String),
    
    #[error("レコードが見つかりません")]
    NotFound,
}

snafu

snafuはより包括的なエラーハンドリングフレームワークを提供し、エラーコンテキストとエラーチェーンに重点を置いています。

特徴:

  • 「コンテキストセレクター」パターンでより正確なエラーコンテキストの追加を促進
  • 「モジュールごとに1つのエラー列挙型」パターンを推奨
  • 構造体スタイルとタプルスタイルのエラーバリアントをサポート
  • 組み込みのバックトレースサポート
use snafu::{Snafu, ResultExt, Backtrace};

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("設定ファイル {filename:?} を読み込めませんでした"))]
    ReadConfig {
        filename: String,
        source: std::io::Error,
        backtrace: Backtrace,
    },
    
    // タプルスタイルも使用可能
    #[snafu(display("IOエラーが発生しました"))]
    Io(#[snafu(source)] std::io::Error, #[snafu(backtrace)] Backtrace),
}

// コンテキストセレクターの使用例
fn read_config(path: &str) -> Result<Config, Error> {
    std::fs::read_to_string(path).context(ReadConfigSnafu { filename: path })?;
    // ...
}

比較

特徴thiserrorsnafu
構文の簡潔さより簡潔やや冗長
エラーコンテキスト基本的なサポート豊富なコンテキスト機構
適応規模小規模から中規模プロジェクト中規模から大規模プロジェクト
コード行数エラーごとに約2行エラーごとに約5行
エラー組織化通常は単一のエラー列挙型モジュールレベルのエラー列挙型を推奨
バックトレースサポート組み込みサポートなし組み込みサポートあり

選択のアドバイス:

  • thiserrorを選ぶ シンプルで明確なエラータイプが必要な場合、特にライブラリ内で
  • snafuを選ぶ より構造化されたエラーハンドリングが必要な場合、特に大規模アプリケーションで

anyhow

anyhowは上記2つとは異なるエラーハンドリングライブラリで、アプリケーションではなくライブラリに焦点を当てています。

特徴:

  • ライブラリではなくアプリケーションのエラーハンドリング向けに設計
  • 動的なanyhow::Error型を提供し、Errorトレイトを実装するあらゆるエラーを含められる
  • 複数のエラータイプにまたがる処理を簡素化
  • カスタムエラータイプの定義が不要
use anyhow::{Context, Result};

fn main() -> Result<()> {
    let config = std::fs::read_to_string("config.json")
        .context("設定ファイルを読み込めませんでした")?;
        
    let app_config: AppConfig = serde_json::from_str(&config)
        .context("設定の形式が無効です")?;
        
    // Result<T>をResult<T, anyhow::Error>の型エイリアスとして使用
    Ok(())
}

anyhow vs thiserror/snafu:

  • anyhowはアプリケーション開発時の迅速なエラーハンドリングに焦点
  • thiserror/snafuは正確なエラータイプ階層の作成に焦点
  • anyhowは通常アプリケーションコードで使用
  • thiserror/snafuは通常ライブラリコードで使用

実際には、anyhowとthiserrorはよく併用されます:ライブラリはthiserrorで正確なエラータイプを定義し、アプリケーションはanyhowで様々なエラーソースを処理します。