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

使用thiserror的transparent属性时,如何断言原始类型错误?

解决方案

首先明确模块A和B的错误定义(基于你的描述补充完整可运行代码):

模块A:几何校验错误定义

mod geometry {
    use thiserror::Error;

    #[derive(Debug, Error)]
    pub enum InvalidGeometryError {
        #[error("图形首尾均位于trailing edge")]
        BeginAndEndAtTrailingEdgeError,
        #[error("顶点数量不足")]
        InsufficientVerticesError,
    }

    pub fn validate_geometry(vertices: &[(f64, f64)]) -> Result<(), InvalidGeometryError> {
        if vertices.len() < 3 {
            return Err(InvalidGeometryError::InsufficientVerticesError);
        }
        if let (Some(first), Some(last)) = (vertices.first(), vertices.last()) {
            if first.1 == 1.0 && last.1 == 1.0 {
                return Err(InvalidGeometryError::BeginAndEndAtTrailingEdgeError);
            }
        }
        Ok(())
    }
}

模块B:读取错误定义(包含模块A的错误)

mod reader {
    use super::geometry::InvalidGeometryError;
    use thiserror::Error;

    #[derive(Debug, Error)]
    pub enum GeometryReadError {
        #[error("IO错误: {0}")]
        IoError(#[from] std::io::Error),
        #[error(transparent)]
        InvalidGeometryError(#[from] InvalidGeometryError),
        #[error("数据格式错误")]
        FormatError,
    }

    pub fn read_geometry_from_file(_path: &str) -> Result<(), GeometryReadError> {
        // 模拟读取后校验失败的场景
        let vertices = vec![(0.0, 1.0), (1.0, 1.0), (0.5, 1.0)];
        geometry::validate_geometry(&vertices)?;
        Ok(())
    }
}

实现断言宏

1. assert_err:断言错误为指定外层变体

这个宏用于校验返回结果是否为目标错误枚举的特定变体:

#[macro_export]
macro_rules! assert_err {
    ($expr:expr, $err_pat:pat) => {
        match $expr {
            Err(e) => match e {
                $err_pat => {},
                _ => panic!(
                    "预期错误变体 `{}`,实际得到 `{:?}`",
                    stringify!($err_pat),
                    e
                ),
            },
            Ok(_) => panic!("预期错误,实际得到 Ok"),
        }
    };
}

2. assert_original_err:断言嵌套的原始错误变体

这个宏用于穿透外层错误,校验内部的原始错误类型及变体:

#[macro_export]
macro_rules! assert_original_err {
    ($expr:expr, $outer_pat:pat => $inner_pat:pat) => {
        match $expr {
            Err(e) => match e {
                $outer_pat(inner_err) => match inner_err {
                    $inner_pat => {},
                    _ => panic!(
                        "预期内部错误变体 `{}`,实际得到 `{:?}`",
                        stringify!($inner_pat),
                        inner_err
                    ),
                },
                _ => panic!(
                    "预期外层错误变体 `{}`,实际得到 `{:?}`",
                    stringify!($outer_pat),
                    e
                ),
            },
            Ok(_) => panic!("预期错误,实际得到 Ok"),
        }
    };
}

测试用例示例

#[cfg(test)]
mod tests {
    use super::{geometry, reader};
    use crate::{assert_err, assert_original_err};

    #[test]
    fn test_read_error_variant() {
        let result = reader::read_geometry_from_file("test.txt");
        // 断言错误是GeometryReadError::InvalidGeometryError
        assert_err!(result, reader::GeometryReadError::InvalidGeometryError(_));
    }

    #[test]
    fn test_original_geometry_error() {
        let result = reader::read_geometry_from_file("test.txt");
        // 断言原始错误是BeginAndEndAtTrailingEdgeError
        assert_original_err!(
            result,
            reader::GeometryReadError::InvalidGeometryError => geometry::InvalidGeometryError::BeginAndEndAtTrailingEdgeError
        );
    }

    #[test]
    fn test_direct_validate_error() {
        let result = geometry::validate_geometry(&vec![(0.0, 0.0), (1.0, 0.0)]);
        // 直接断言InvalidGeometryError的变体
        assert_err!(result, geometry::InvalidGeometryError::InsufficientVerticesError);
    }
}

宏的工作逻辑

  • assert_err:先检查表达式是否返回Err,再匹配指定的错误变体,不匹配则输出清晰的panic信息。
  • assert_original_err:先匹配外层错误变体并提取内部错误,再校验内部错误的目标变体,两层校验确保错误传递的正确性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 21:35:01