thiserror:Rust库层错误处理的derive方案,5.4k Star
David Tolnay 的 thiserror 在 GitHub 上获得了 5,425 个 Star。这个库通过 derive 宏自动生成 std::error::Error trait 的实现,开发者无需手写 Display 转换和 source 追溯逻辑。
在 Rust 中定义自定义错误类型,通常需要为每个变体手动实现 Display 和 Error trait。枚举变体一多,样板代码会快速膨胀,出错概率也同步增加。thiserror 在编译期通过过程宏生成这些实现,写出来的代码紧凑,且不暴露到公共 API 中。
核心用法是在枚举或结构体上添加 #[derive(Error)],然后用 #[error("...")] 为每个变体指定错误消息:
use thiserror::Error;
#[derive(Error, Debug)]
pub enum DataStoreError {
#[error("data store disconnected")]
Disconnect(#[from] io::Error),
#[error("the data for key `{0}` is not available")]
Redaction(String),
#[error("invalid header (expected {expected:?}, found {found:?})")]
InvalidHeader {
expected: String,
found: String,
},
#[error("unknown data store error")]
Unknown,
}
#[error("...")] 中的消息支持字段插值。{var} 对应 self.var 的 Display 输出,{var:?} 对应 Debug 输出。元组结构体用 {0} 引用位置字段。插值后方可追加额外的格式化参数,参数可以是任意表达式:
#[derive(Error, Debug)]
pub enum Error {
#[error("invalid rdo_lookahead_frames {0} (expected < {max})", max = i32::MAX)]
InvalidLookahead(u32),
}
外部表达式中引用字段时,命名字段用 .var,元组字段用 .0:
#[derive(Error, Debug)]
pub enum Error {
#[error("first letter must be lowercase but was {:?}", first_char(.0))]
WrongCase(String),
#[error("invalid index {idx}, expected at least {} and at most {}", .limits.lo, .limits.hi)]
OutOfBounds { idx: usize, limits: Limits },
}
thiserror 还处理了 Error trait 中 Display 以外的几个方法:
source() 方法自动指向标记了 #[source] 或 #[from] 的字段。#[from] 属性同时为对应变体生成 From 实现,错误传播只需 ? 就能完成类型转换。带有 #[from] 的变体不能包含源头错误之外的其他字段,backtrace 例外。
需要透传底层错误时,#[error(transparent)] 将 Display 和 source 方法委托给内层错误,不添加额外消息。适合枚举中的兜底变体,或者将内部表示隐藏在公开类型后面,保持 API 兼容性的同时自由调整实现。
在 1.73 以上版本的 nightly 编译器上,thiserror 支持通过 Backtrace 类型自动捕获回溯。如果某字段既是 source 又被标记为 #[backtrace],provide() 会转发到 source 的同名方法,错误链中多层共享同一个回溯。
thiserror 的设计核心是不出现在公共 API 中。使用 thiserror 生成的结果与手写实现等价,在两者间切换不构成破坏性变更。库作者可以在不担心下游兼容性的前提下引入。
与同一个作者开发的 anyhow 对比,thiserror 面向库代码,适合需要为调用方提供明确错误信息的设计。anyhow 面向应用代码,适合不关心具体错误类型、只希望方便传播错误的场景。两者分工明确:库用 thiserror,应用用 anyhow。
引入方式:
[dependencies]
thiserror = "2"
对于 Rust 生态中需要定义专用错误类型的项目,thiserror 将错误处理的样板代码压缩到几行属性宏。5,425 个 Star 和广泛的社区使用,证明这套 derive 方案已成为 Rust 错误处理中的常用组件。