让我们从这个minimal, reproducible example开始:
use snafu::Snafu;
#[derive(Debug, Snafu)]
enum Error {
SampleError { msg: String },
}
type Result<T, E = Error> = std::result::Result<T, E>;
fn alpha() -> Result<()> {
beta()
}
fn beta() -> Result<()> {
gamma()
}
fn gamma() -> Result<()> {
SampleError { msg: "foo" }.fail()
}
注意它使用上下文选择器SampleError和方法fail而不是直接使用枚举变体来构造错误。
现在我们导入 snafu::Backtrace 并将其添加到我们的错误中,将其命名为 backtrace(如果您必须将其命名为其他名称,请参阅 controlling backtraces)。
use snafu::{Snafu, Backtrace};
#[derive(Debug, Snafu)]
enum Error {
SampleError { msg: String, backtrace: Backtrace },
}
如果这是一个图书馆,那就是你应该停下来的地方。如果 二进制文件 认为回溯是值得的,您的错误现在将可选启用回溯。这是因为回溯在 Rust 中尚未稳定,因此 SNAFU 必须与多种可能的实现兼容。
如果您要控制二进制文件,则需要决定如何实现回溯。功能标志选择了三个主要实现:
-
backtraces — 提供不透明的 Backtrace 类型
-
backtraces-impl-backtrace-crate — 使用第三方 backtrace crate。 snafu::Backtrace 只是 backtrace::Backtrace 的别名。
-
unstable-backtraces-impl-std — 使用不稳定的标准库 Backtrace。 snafu::Backtrace 只是 std::backtrace::Backtrace 的别名。
一旦您选择了实现功能标志,请将其添加到您的 Cargo.toml:
[dependencies]
snafu = { version = "0.6.3", features = ["backtraces"] }
然后,您需要在程序中的某个位置处理错误并获取回溯并打印出来。这使用了 ErrorCompat 特征,我鼓励您以详细的方式使用它,以便以后在标准库中稳定时更容易将其删除:
use snafu::ErrorCompat;
fn main() {
if let Err(e) = alpha() {
if let Some(bt) = ErrorCompat::backtrace(&e) {
println!("{:?}", bt);
}
}
}
0: backtrace::backtrace::trace_unsynchronized
1: backtrace::backtrace::trace
2: backtrace::capture::Backtrace::create
3: backtrace::capture::Backtrace::new
4: <backtrace::capture::Backtrace as snafu::GenerateBacktrace>::generate
5: so::SampleError<__T0>::fail
6: so::gamma
7: so::beta
8: so::alpha
9: so::main
10: std::rt::lang_start::{{closure}}
11: std::panicking::try::do_call
12: __rust_maybe_catch_panic
13: std::rt::lang_start_internal
14: std::rt::lang_start
15: main
免责声明:我是 SNAFU 的主要作者。
你说得对,这在the user's guide 中没有完全描述,我已经创建了an issue to improve that。最相关的部分是关于feature flags 的部分。
您可以查看 SNAFU 存储库中的多个回溯测试: