---
title: Error trait とエコシステム
url: https://doc.liz6.com/ja/rust/05-error-handling/02-error-trait-and-ecosystem
locale: ja
area: rust
tags:
- rust
- error-handling
date: 2026-06-30
modified: 2026-07-16
description: std::error::Error は Display+Debug+source() の組み合わせに過ぎず、薄すぎる。thiserror は derive マクロを使ってライブラリに構造化されたエラー型を生成し（呼び出し元が match 可能）、anyhow はアプリケーション向けに「エラー型を気にせず、ただ上に伝播させる」万能なエラーラッパーを提供する。ライブラリには thiserror、アプリケーションには anyhow——これは教義ではなく、実践から蓄積された役割分担である。
---

# Error trait とエコシステム

> std::error::Error は Display+Debug+source() の組み合わせに過ぎず、薄すぎる。thiserror は derive マクロを使ってライブラリに構造化されたエラー型を生成し（呼び出し元が match 可能）、anyhow はアプリケーション向けに「エラー型を気にせず、ただ上に伝播させる」万能なエラーラッパーを提供する。ライブラリには thiserror、アプリケーションには anyhow——これは教義ではなく、実践から蓄積された役割分担である。

## std::error::Error trait

標準ライブラリの `Error` trait は非常にシンプルだ——`Display + Debug + source()`：

```rust
use std::error::Error;
use std::fmt;

#[derive(Debug)]
struct MyError { msg: String, source: Option<Box<dyn Error + 'static>> }

impl fmt::Display for MyError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", self.msg)
}}

impl Error for MyError {
    fn source(&self) -> Option<&(dyn Error + 'static)> { self.source.as_deref() }
}
```

`source()` メソッドは根本原因を返す——これによりエラーチェーンが形成される。`anyhow` と `eyre` はこのチェーンを利用してバックトレースを自動生成する。

## thiserror: ライブラリクレートの derive マクロ

**ライブラリ**向け——呼び出し元にマッチ可能な具体的なエラー型を提供する：

```rust
use thiserror::Error;

#[derive(Error, Debug)]
pub enum DataStoreError {
    #[error("data not found: {0}")]
    NotFound(String),
    #[error("IO error")]
    Io(#[from] std::io::Error),      // #[from] = From の自動実装 + 自動 source
    #[error("invalid config key={key}")]
    InvalidConfig { key: String, detail: String },
}
```

`#[from]` は `From<io::Error> for DataStoreError` を自動生成し、`?` による自動変換を可能にする。呼び出し元は `match` を使ってエラー型を区別できる。

## anyhow: アプリケーションコードの汎用エラーコンテナ

**アプリケーション/バイナリ**向け——エラーチェーンを収集し、具体的な型にはこだわらない：

```rust
use anyhow::{Context, Result};

fn read_config(path: &str) -> Result<Config> {
    let file = std::fs::read_to_string(path)
        .with_context(|| format!("failed to read config from {}", path))?;
    let config: Config = toml::from_str(&file)
        .context("invalid TOML format")?;
    Ok(config)
}
```

`anyhow::Error` は任意の `Error + Send + Sync + 'static` を格納できる。`context()` はエラーチェーンに人間が読める注釈を追加する——これが anyhow で最もよく使われるパターンだ。

## thiserror と anyhow の使い分け

シンプルなルールがある：**ライブラリは thiserror（呼び出し元に選択肢を）、アプリケーションは anyhow（エラーの文脈を収集）**。ライブラリが呼び出し元に特定のエラー報告ライブラリを強制すべきではない——具体的でマッチ可能な型を投げればいい。アプリケーションはエラー型を区別する必要はない——必要なのは、すべてのエラーがキャッチされ、記録され、バグを再現するのに十分な文脈を持っていることだ。

## 参考

- **thiserror**: docs.rs/thiserror
- **anyhow**: docs.rs/anyhow

*Keywords: Error trait, thiserror, anyhow, context, source, error chain, #[from]*
