---
title: Error trait 与生态
url: https://doc.liz6.com/rust/05-error-handling/02-error-trait-and-ecosystem
locale: zh
area: rust
tags:
- rust
- 错误处理
date: 2026-06-30
modified: 2026-06-30
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` 利用这个链自动生成 backtrace。

## thiserror: library crate 的 derive macro

用于**库**——给 caller 提供可匹配的具体错误类型：

```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] = 自动 impl From + 自动 source
    #[error("invalid config key={key}")]
    InvalidConfig { key: String, detail: String },
}
```

`#[from]` 自动生成 `From<io::Error> for DataStoreError`，使 `?` 可以自动转换。caller 可以用 `match` 区分错误类型。

## anyhow: application code 的通用错误容器

用于**应用/二进制**——收集错误链，不关心具体类型：

```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 vs anyhow

一条简单规则：**库用 thiserror（给 caller choice），应用用 anyhow（收集错误上下文）**。库不应该强制 caller 使用特定的错误报告库——抛具体的、可匹配的类型。应用不需要区分错误类型——需要的是所有错误都被捕获、记录，并有足够的上下文重现 bug。

## 参考

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

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