Rust 错误处理分层:用 thiserror 定义库错误、用 anyhow 收口应用层
test12026-09-206 次阅读
库层与应用层,错误的职责不一样
Rust 错误处理常被新手搞成一坨 anyhow::Result。更清晰的分工是:底层库/模块用 thiserror 定义成体系的错误类型,让调用方可以 match 区分;最上层的 main 或 handler 用 anyhow 收口,只关心“成不成功”以及失败时带上上下文。两层配合,既不丢失可判别性,也不让上层被错误类型淹没。
用 thiserror 定义可判别的库错误
#[from] 自动生成 From 实现,使底层 sqlx::Error 能 ? 直接上转;NotFound、InvalidInput 则是业务自己的变体,调用方可以精确匹配。Display 信息也在这里写清楚。
use thiserror::Error;
#[derive(Debug, Error)]
pub enum RepositoryError {
#[error("数据库连接失败: {0}")]
Db(#[from] sqlx::Error),
#[error("记录不存在: id={0}")]
NotFound(i64),
#[error("参数非法: {0}")]
InvalidInput(String),
}
pub fn find_user(id: i64) -> Result<(), RepositoryError> {
if id <= 0 {
return Err(RepositoryError::InvalidInput("id 必须为正".into()));
}
if id == 999 {
return Err(RepositoryError::NotFound(id));
}
Ok(())
}
用 anyhow 在上层收口并附上下文
应用层一般不想再为每一种可能错误定义新类型。anyhow::Result 配合 with_context,能在出错时把“这一步在做什么”串进报错,排障时一眼看到失败链路。
use anyhow::{Context, Result};
fn load_config(path: &str) -> Result<String> {
let raw = std::fs::read_to_string(path)
.with_context(|| format!("读取配置文件失败: {path}"))?;
Ok(raw)
}
fn main() -> Result<()> {
let cfg = load_config("config.toml")?;
println!("配置长度: {}", cfg.len());
Ok(())
}
怎么选
- 被别人依赖的 crate / 模块:用 thiserror,错误要可枚举、可 match。
- 二进制入口、HTTP handler、脚本:用 anyhow,只问成败并附上下文。
- 需要跨层转换时,用 #[from] 或 map_err 把底层错误提升为上层类型。
T
test1
文章作者
库层与应用层,错误的职责不一样 Rust 错误处理常被新手搞成一坨 anyhow::Result。更清晰的分工是:底层...
- 分类
- 技术
- 发布时间
- 2026-09-20
- 字数
- 约 1445 字
- 阅读
- 6 次
