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で様々なエラー源を処理します。