在rust中,如果有一棵深层的函数树都调用io :: Result类型的所有调用IO操作,并使用问号运算符进行错误处理,那么如何获取堆栈跟踪以查看哪个IO操作失败?
例如:
fn func2(param: u32) -> io::Result<u32> {
perform_io1(param)?;
perform_io2(param + 10)
}
fn func1(param: String) -> io::Result<u32> {
let v = perform_io3(param)?;
perform_io4(v)?;
let v2 = func2(v * 3)?;
perform_io5()
}
fn main() {
func1("test".to_string()).
expect("func1 failed");
}
运行时,如果func1()或func2()中的IO操作之一失败,则会出现类似以下错误:
thread 'main' panicked at 'func1 failed: Os { code: 16, kind: Other, message: "Device or resource busy" }', src/libcore/result.rs:1084:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
如果使用“ RUST_BACKTRACE = 1”执行,则回溯并没有太大帮助:
<snip>
8: rust_begin_unwind
at src/libstd/panicking.rs:311
9: core::panicking::panic_fmt
at src/libcore/panicking.rs:85
10: core::result::unwrap_failed
at src/libcore/result.rs:1084
11: core::result::Result<T,E>::expect
at /rustc/625451e376bb2e5283fc4741caa0a3e8a2ca4d54/src/libcore/result.rs:879
12: example::main
at src/main.rs:14
13: std::rt::lang_start::{{closure}}
at /rustc/625451e376bb2e5283fc4741caa0a3e8a2ca4d54/src/libstd/rt.rs:64
14: std::rt::lang_start_internal::{{closure}}
at src/libstd/rt.rs:49
<snip>
问题是它不会告诉您哪个io操作导致了失败。任何设备操作都可能导致“设备或资源繁忙”。
很棒的事情是得到这样的东西:
thread 'main' panicked at 'func1 failed: Os { code: 16, kind: Other, message: "Device or resource busy" }',
- src/libcore/result.rs:1084:5
- src/main.rs:3 - perform_io2(param + 10)
- src/main.rs:9 - func2(v * 3)
- src/main.rs:14 - func1("test".to_string())
当然,这意味着?操作员需要将故障封装到每个传递的io:Err中,直到传递给Expect()。
在Rust中实现这一目标的最干净的方法是什么?理想情况下,不必为每个io操作的结果添加匹配表达式即可记录结果并将其传递回去。或者只是使用unwrap()而不是? (这将防止在main()中更优雅地处理该错误)。
答案 0 :(得分:0)
当然,在搜索了一段时间并最终提出问题之后,我在30分钟后找到了解决方案:
输入error_chain条板箱:
因此生成的代码必须类似于:
#![recursion_limit = "1024"]
#[macro_use]
extern crate error_chain;
mod errors {
error_chain!{}
}
use errors::*;
fn func2(param: u32) -> Result<u32> {
perform_io1(param).chain_err(|| "failed to perform io1")?;
perform_io2(param + 10).chain_err(|| "failed to perform io2")
}
fn func1(param: String) -> Result<u32> {
let v = perform_io3(param).chain_err(|| "failed to perform io3")?;
perform_io4(v).chain_err(|| "failed to perform io4")?;
let v2 = func2(v * 3).chain_err(|| "func2 failed")?;
perform_io5().chain_err(|| "failed to perform io5")
}
fn main() {
if let Err(ref e) = run() {
use std::io::Write;
let stderr = &mut ::std::io::stderr();
let errmsg = "Error writing to stderr";
writeln!(stderr, "error: {}", e).expect(errmsg);
for e in e.iter().skip(1) {
writeln!(stderr, "caused by: {}", e).expect(errmsg);
}
if let Some(backtrace) = e.backtrace() {
writeln!(stderr, "backtrace: {:?}", backtrace).expect(errmsg);
}
::std::process::exit(1);
}
}
fn run() -> Result<()> {
use std::fs::File;
// This operation will fail
func1("test".to_string()).
.chain_err(|| "func1 failed")?;
Ok(())
}
基于:the template recommended by Brian Anderson
当然,在实际示例中,错误消息应该看起来更好。