如何从Box<dyn StdError>中恢复经anyhow::Error转换后的原始错误类型?
如何从Box中恢复经anyhow::Error转换后的原始错误类型?
嘿,我刚好碰到过类似的问题,其实你直接downcast失败的核心原因很简单:当你把SqlxError转成anyhow::Error再装箱成Box<dyn StdError>后,这个boxed错误的实际类型已经是anyhow::Error了——它把你的原始SqlxError给包装在内部,直接跳过它去downcast原始类型当然找不到啦。
我给你分享两个亲测有效的解决办法,先补全你没写完的示例代码,方便你对照:
use std::error::Error as StdError; use std::fmt; #[derive(Debug)] struct SqlxError { message: String, } impl fmt::Display for SqlxError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.message) } } impl StdError for SqlxError {} fn main() -> anyhow::Result<()> { let original_err = SqlxError { message: "database connection failed".to_string(), }; // 转成anyhow::Error再装箱 let anyhow_err: anyhow::Error = original_err.into(); let boxed_err: Box<dyn StdError + Send + Sync + 'static> = anyhow_err.into(); // 这里直接downcast会失败 if boxed_err.downcast_ref::<SqlxError>().is_some() { println!("Recovered SqlxError directly"); } else { println!("Failed to downcast directly"); // 会打印这个 } Ok(()) }
解决办法一:先转回anyhow::Error再提取原始错误
既然现在boxed里是anyhow::Error,那我们先把它转回来,再用anyhow自带的downcast方法拿原始错误:
// 替换main函数里的downcast尝试部分 if let Ok(mut anyhow_err) = boxed_err.downcast::<anyhow::Error>() { // 用anyhow的downcast_mut获取可变引用,或者downcast获取所有权 if let Some(sqlx_err) = anyhow_err.downcast_mut::<SqlxError>() { println!("Recovered SqlxError: {:?}", sqlx_err); } else { println!("Original error isn't a SqlxError"); } } else { println!("This boxed error isn't from anyhow"); }
这个思路很直接:先拿到anyhow的错误包装器,再从它里面掏出我们真正需要的原始错误。anyhow内部保留了完整的类型信息,所以这一步肯定能成。
解决办法二:遍历错误链查找目标类型
如果你的错误存在多层嵌套(比如原始SqlxError被anyhow包装,anyhow又被其他错误包装了一层),那可以用chain方法遍历整个错误链,逐个检查每个错误是不是我们要的类型:
// 同样替换main里的downcast部分 for err in boxed_err.chain() { if let Some(sqlx_err) = err.downcast_ref::<SqlxError>() { println!("Found SqlxError in error chain: {:?}", sqlx_err); break; } }
chain方法会从最外层错误开始,一直遍历到最原始的那个错误,我们用downcast_ref检查每个节点,找到目标类型就停下就行。
一些要注意的小细节
- 一定要确保你的原始错误类型(比如SqlxError)正确实现了
StdErrortrait,不然anyhow没法正确包装它,后续的downcast也会出问题 - 如果你的代码里可能混合了anyhow包装的错误和其他直接装箱的错误,一定要先判断当前boxed错误是不是anyhow::Error类型,避免downcast失败导致panic(用
downcast的返回值判断,不要用unwrap)
备注:内容来源于stack exchange,提问作者Anunaki
相关产品推荐
相关产品推荐

