使用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
相关产品推荐
相关产品推荐

