我可以看到几个关于 Rust 中传播错误的问题,但没有一个真正与我想要实现的目标有关。
开发第一个小型 Rust 项目时,我已经养成了使用此返回签名的习惯:
-> Result<[something from success], Box<dyn std::error::Error>> {
...
...并在这些方法的主体中尽可能多地使用
?
运算符。
但现在我发现有时很难找出错误来自哪里,即使使用
backtrace::Backtrace
也是如此。事实上这是一个 PyO3 项目,所以主要的“入口点”看起来像这样:
#[pyfunction]
fn index_documents(py: Python, dir_root_path_str: String, index_name: String, op_type: String, app_version_str: String,
index_version_str: String, option_suspended_framework: Option<HandlingFramework>)
-> PyResult<(usize, usize, Option<HandlingFramework>)> {
py.allow_threads(move || {
match index_docs_rust(dir_root_path_str, index_name, op_type, app_version_str, index_version_str, option_suspended_framework) {
Ok(three_tuple) => Ok(three_tuple),
Err(e) => {
error!("backtrace:\n{}", Backtrace::force_capture());
Err(PyErr::new::<PyTypeError, _>(e.to_string()))
}
}
})
}
fn index_docs_rust( ...
-> Result<(usize, usize, Option<HandlingFramework>), Box<dyn std::error::Error>> {
// makes structs, which call multiple methods, create other structs, which call their
// own multiple methods, etc.
在
force_capture
中生成了很多行,但实际上没有一个为您提供堆栈跟踪(即之后)
index_documents
中的行,即使在进行开发编译时也是如此。据我了解(不多),我相信与 Python 不同,跟踪信息实际上并不随错误对象一起传输。
我找到了这个答案
,这可能是尽可能好的解决方案。但这样做的麻烦在于(看起来)你必须将“预期”代码放在可能发生错误的任何地方......这会破坏对象:你不妨使用
match
块并将内容记录在
Err
分支。但事实上,这并不是全面有用的:真正有用的堆栈跟踪将向您准确显示(如在 Python 中)代码在错误发生之前通过多种方法采用的完整路径。
Rust 有什么方法可以做到这一点吗?
感谢 kmdreko 提供的解决方案。详细内容如下。
在 Cargo.toml 中:
anyhow = { version = "*", features = ["backtrace"] }
添加永久或临时环境变量:
(SET) RUST_BACKTRACE=1
(注意可能还有其他方法可以打开回溯......)
在每个文件中:
use anyhow::{anyhow, Result};
...这似乎会在该文件的持续时间内覆盖
std::result::Result
。所以现在你必须使用仅带有一个类型参数的Result
。
典型方法签名:
fn do_something(&self, param: String) -> anyhow::Result<()> {
...
然后您可以在该方法的主体中使用
?
并进行适当的函数调用。
您可以按如下方式“包装”
String
或Error
(不是感叹号:这是一个宏):
return Err(anyhow!(msg))
或
return Err(anyhow!(e))
或者(
lock()
方法有点复杂):
let mut vec = my_mutex.lock().map_err(|_| anyhow!("lock poisoned!"))?;
在调用路径顶部的函数中:
...
match self.do_something(my_string) {
Ok() => Ok(),
Err(e) => {
error!("e {:#?}\ne.backtrace():\n{}", e, e.backtrace());
Err(e)
}
}
然后将完整的堆栈跟踪打印到错误行,包括行号。最初似乎不包括发生错误的实际行号(只是对该方法的调用),但在重新检查时,我现在看到跟踪实际上给出了该行号...
此外,我找不到将
Box<dyn std::error::Error>
“转换”为 anyhow::Error
的方法。因此,整个模块确实必须切换为使用 anyhow
的处理方式。