如何让Box<dyn std::error::Error>通过?获取错误发生行号?
适配Box的行号错误实现
要让Box<dyn std::error::Error>带上错误发生的行号,我们可以通过自定义错误结构体+宏的方式实现,满足你期望的代码逻辑(稍作调整以适配位置捕获):
1. 定义带位置的错误结构体
首先定义LocatedError,用于包裹原始错误和调用位置:
use std::error::Error; use std::fmt; use std::panic::Location; #[derive(Debug)] struct LocatedError { inner: Box<dyn Error>, location: &'static Location<'static>, }
2. 实现Display和Error trait
让错误能正确打印,同时保留原始错误的源信息:
impl fmt::Display for LocatedError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "错误: {},发生在: {}", self.inner, self.location) } } impl Error for LocatedError { fn source(&self) -> Option<&(dyn Error + 'static)> { self.inner.source() } }
3. 通用错误转换与位置捕获
实现一个通用转换函数,把任意错误类型包装成LocatedError并捕获当前位置:
trait IntoLocatedError { fn into_located(self) -> LocatedError; } impl<E: Error + 'static> IntoLocatedError for E { fn into_located(self) -> LocatedError { LocatedError { inner: Box::new(self), location: Location::caller(), } } } // 为Box<dyn Error>实现转换,支持嵌套错误 impl IntoLocatedError for Box<dyn Error> { fn into_located(self) -> LocatedError { LocatedError { inner: self, location: Location::caller(), } } }
4. 自定义宏替代?运算符
由于标准?无法直接捕获调用位置,我们用一个宏实现类似?的功能,自动添加位置信息:
macro_rules! try_loc { ($expr:expr) => { match $expr { Ok(val) => val, Err(e) => return Err(e.into_located()), } }; }
5. 使用示例
现在可以按照需求编写代码,只需把?替换为try_loc!:
fn main() -> Result<(), LocatedError> { try_loc!(std::fs::File::open("blurb.txt")); Ok(()) }
运行后,如果文件不存在,会输出类似:
错误: No such file or directory (os error 2),发生在: src/main.rs:25:5
补充说明
如果一定要用标准?运算符,需要自定义Result类型并实现From trait,但这样无法捕获正确的调用位置(Location::caller()会指向From实现的位置而非用户代码的?位置),所以宏是更可靠的方案。
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

