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

使用thiserror和anyhow时如何保留错误上下文?

解决自定义错误与上下文扩展的冲突问题

核心问题根源

一旦用anyhow::context给错误添加上下文,错误会被包装成anyhow::Error类型——这个通用错误容器不了解你用thiserror定义的自定义枚举结构,自然无法自动映射回去。直接实现From<anyhow::Error>又会丢失原始错误的类型信息,导致上下文和具体错误变体的关联断裂。

解决方案1:基于thiserror扩展上下文

无需依赖anyhow,给自定义错误枚举添加带上下文的变体,或为原有变体增加上下文字段:

方式A:通用上下文变体

use thiserror::Error;

#[derive(Error, Debug)]
pub enum BufferPoolError {
    #[error("buffer exhausted")]
    BufferExhausted,
    #[error("split table failed: {0}")]
    SplitTableError(#[from] ExtendibleHashTableSplitErrors),
    #[error("{context}: {source}")]
    WithContext {
        context: String,
        source: Box<dyn std::error::Error + Send + Sync>,
    },
}

// 定义扩展trait简化上下文添加逻辑
pub trait AddContext<T> {
    fn with_context<C: Into<String>>(self, context: C) -> Result<T, BufferPoolError>;
}

impl<T, E: std::error::Error + Send + Sync + 'static> AddContext<T> for Result<T, E> {
    fn with_context<C: Into<String>>(self, context: C) -> Result<T, BufferPoolError> {
        self.map_err(|source| BufferPoolError::WithContext {
            context: context.into(),
            source: Box::new(source),
        })
    }
}

方式B:为特定变体添加上下文字段

如果需要更精准的上下文关联,直接给目标变体增加字段:

#[derive(Error, Debug)]
pub enum BufferPoolError {
    #[error("buffer exhausted: {reason}")]
    BufferExhausted { reason: String },
    #[error("split table failed: {source} (context: {context})")]
    SplitTableError {
        source: ExtendibleHashTableSplitErrors,
        context: String,
    },
}

// 使用示例
fn allocate_buffer() -> Result<(), BufferPoolError> {
    Err(BufferPoolError::BufferExhausted {
        reason: "requested 8 buffers, only 3 available".into(),
    })
}

解决方案2:正确使用snafu实现上下文

snafu的核心是通过错误变体的字段承载上下文,而非依赖外部包装。重新定义错误枚举:

use snafu::Snafu;

#[derive(Debug, Snafu)]
pub enum BufferPoolError {
    #[snafu(display("buffer exhausted: {reason}"))]
    BufferExhausted { reason: String },
    #[snafu(display("split table failed: {source} (context: {context})"))]
    SplitTableError {
        source: ExtendibleHashTableSplitErrors,
        context: String,
    },
}

// 使用示例:链式添加上下文
fn split_table() -> Result<(), BufferPoolError> {
    // 假设split_inner返回ExtendibleHashTableSplitErrors
    split_inner().context(SplitTableErrorSnafu {
        context: "trying to split bucket 42".into(),
    })
}

关键注意事项

  • 避免混用anyhow和自定义错误枚举:anyhow适合快速原型或无需精准错误匹配的场景,自定义错误则用于需要精准处理错误的业务逻辑。
  • 上下文要与错误变体绑定:无论是thiserror还是snafu,上下文都应作为错误变体的一部分存在,而非用外部容器包装,这样才能保留完整的错误类型信息。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 02:00:16