跳到主要内容

错误处理 crate 生态

问题

Rust 错误处理有哪些流行的 crate?如何选择?

答案

标准库提供 std::error::Error trait,但实际开发中几乎都要用第三方 crate 来简化错误处理。

关于 Rust 错误处理基础,请参考 错误处理

thiserror:定义库错误

用于库开发,通过 derive 宏生成 Error + Display 实现:

use thiserror::Error;

#[derive(Debug, Error)]
enum AppError {
#[error("数据库错误: {0}")]
Database(#[from] sqlx::Error),

#[error("找不到用户: {id}")]
UserNotFound { id: u64 },

#[error("认证失败")]
Unauthorized,

#[error("IO 错误")]
Io(#[from] std::io::Error),
}

anyhow:应用错误

用于应用开发,提供类型擦除的 anyhow::Error,无需定义枚举:

use anyhow::{Context, Result, bail, ensure};

fn read_config(path: &str) -> Result<Config> {
let content = std::fs::read_to_string(path)
.context(format!("读取配置文件失败: {}", path))?;

let config: Config = toml::from_str(&content)
.context("解析 TOML 失败")?;

ensure!(config.port > 0, "端口号必须为正数");

if config.name.is_empty() {
bail!("名称不能为空"); // 直接返回错误
}

Ok(config)
}

选择指南

crate场景特点
thiserror库开发类型化错误、#[from] 自动转换
anyhow应用开发类型擦除、.context() 链、快速开发
eyreanyhow 替代可自定义报告格式、color-eyre
mietteCLI 工具花哨的错误诊断报告
最佳实践
  • 写库:用 thiserror 定义结构化错误类型,让调用者可以 match
  • 写应用:用 anyhow 快速传播错误,用 .context() 添加上下文
  • 两者可以混用:库用 thiserror,应用入口用 anyhow

常见面试问题

Q1: thiserror 和 anyhow 能一起用吗?

答案

可以且推荐。库层用 thiserror 定义精确的错误类型,应用层用 anyhow 统一处理:

// 库层
#[derive(thiserror::Error, Debug)]
pub enum DbError { /* ... */ }

// 应用层
fn main() -> anyhow::Result<()> {
let conn = db::connect()
.context("数据库连接失败")?; // DbError 自动转为 anyhow::Error
Ok(())
}

Q2: 什么时候不该用 anyhow

答案

  • 写公共库时:调用者无法 match 具体错误类型
  • 需要根据错误类型做不同处理时:类型擦除后无法精确匹配(虽然可以 downcast,但不推荐)

相关链接