You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 },
}

方案优势

  1. 无任何冗余代码:公共字段仅定义一次,后续新增错误类型只需要在ErrorKind里加一行变体即可
  2. 使用体验和枚举变体方案完全一致:可以通过match直接匹配错误类型,也可以随时访问错误的行号、位置等信息
  3. 兼容各类错误处理库:如果要用Snafu之类的库,只需要给ErrorKind或者顶层结构体加对应的派生宏即可,不需要改动分层结构

如果确实想要用枚举变体的写法,也可以用声明宏批量生成所有变体的定义和Display匹配分支,不过分层的方案可读性和维护性远高于宏方案,优先推荐使用。

内容的提问来源于stack exchange,提问作者Genarito

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 22:45:04