Rust中携带公共字段的自定义错误最优实现策略
解决方案
你完全不需要为每个枚举变体重复定义公共字段,最简洁的方案是将公共字段和错误类型分离:
- 先定义仅描述错误类型的枚举
ErrorKind - 再定义顶层错误结构体,同时包含错误类型、公共字段两个部分
代码实现示例
use std::fmt; // 仅存放错误类型,无冗余字段 #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum ErrorKind { ErrorOne, ErrorTwo, // 后续新增错误类型只需要在这里加变体即可 } // 顶层错误结构体,公共字段仅定义一次 #[derive(Debug)] struct MyCustomError { kind: ErrorKind, pos: usize, line: usize, message: String, } impl fmt::Display for MyCustomError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{} at line {} position {}", self.message, self.line, self.pos ) } } impl std::error::Error for MyCustomError {} // 抛出错误的示例 fn throw_error() -> Result<(), MyCustomError> { Err(MyCustomError { kind: ErrorKind::ErrorOne, pos: 22, line: 1, message: String::from("Error one occurred"), }) } fn main() { match throw_error() { Ok(()) => println!("It works!"), Err(err) => { // 直接匹配kind即可区分错误类型,同时可以直接访问所有公共字段 match err.kind { ErrorKind::ErrorOne => println!("Error one -> {}!", err), ErrorKind::ErrorTwo => println!("Error two -> {}!", err), } } } }
如果部分错误类型需要携带专属的额外信息,只需要给ErrorKind对应的变体加字段即可,公共字段仍然保持在顶层结构体中,不会产生冗余:
#[derive(Debug, Clone, Copy, PartialEq, Eq)] enum ErrorKind { ErrorOne, // 带专属字段的错误类型,无需重复公共字段 ErrorTwo { expected: char, found: char }, }
方案优势
- 无任何冗余代码:公共字段仅定义一次,后续新增错误类型只需要在
ErrorKind里加一行变体即可 - 使用体验和枚举变体方案完全一致:可以通过match直接匹配错误类型,也可以随时访问错误的行号、位置等信息
- 兼容各类错误处理库:如果要用Snafu之类的库,只需要给
ErrorKind或者顶层结构体加对应的派生宏即可,不需要改动分层结构
如果确实想要用枚举变体的写法,也可以用声明宏批量生成所有变体的定义和Display匹配分支,不过分层的方案可读性和维护性远高于宏方案,优先推荐使用。
内容的提问来源于stack exchange,提问作者Genarito
相关产品推荐
相关产品推荐

