C9 错误处理
Rust 有两类错误, 包括可恢复, 不可恢复两种. 分别可用 panic!
和 Result<T, E>
表示.
https://doc.rust-lang.org/book/ch09-00-error-handling.html
在 Rust 中没有 exception 的概念, 而是通过上述两种类型结合实现 exception.
不可恢复错误 panic
在 Rust 中, 有两种 panic 的可能性:
- 在代码中触发 panic(比如数组下标访问越界)
- 主动使用
panic!
宏
默认情况下, panic 时, 会打印失败信息, 进行 unwind, 清理 stack, 然后退出.
打印失败信息时, 可以将整个调用栈情况打印出来, 只需要在运行时设置环境变量 RUST_BACKTRACE=1 cargo run
即可. 当非 --release
build 或 run 时, 即可自动生成 debug 符号, 这样调用栈信息可以原样打印出来.
关于何时使用 panic 的讨论, 详见后面内容.
关于 unwind 和 abort
当 panic 发生时, 程序会开始 unwinding, 即 Rust 会顺着栈往回走, 将每个栈帧的数据进行清理. 但这样也意味着可能会有大量的操作在 unwind 时进行.
因此 rust 中可以自定义 panic 后的行为: unwind 或 abort.
abort 即直接退出而不进行清理, 意味着程序使用的内存将由操作系统进行清理. 当希望程序包体积能尽可能小的时候, 就可以将 panic 后行为设置为 abort.
设置 abort 的方法也非常简单, 只需要在 [profile]
部分写一行 panic = 'abort'
即可.
可恢复错误 Result
可以使用 Result<T, E>
表示可恢复错误, 其中 T 为正常返回值, E 为错误值. 可以通过 match 来针对不同错误进行处理:
let res = match call_result {
Ok(_) => (),
Err(e) => match e.kind() {
ErrorKind::NotFound => {
// ...
},
other_error => {
panic!("error! {:?}", e);
}
}
}
// 当然我们也可以通过 `unwrap`, `unwrap_or_else` 或 `if e.kind() == ErrorKind::NotFound {}` 等手段来对错误进行处理.
可以使用 ?
操作符对错误进行传递, 这样可以极大减少代码量, 且更易读. ?
操作符的意思是 "出错返回 Result, 否则拿到结果值"(针对 Result
返回类型), 或 "没有返回 None, 否则拿到结果值"(针对 Option
返回类型).
能够使用 ?
操作符的函数/方法, 必须是 Result
/Option
或任何实现 FromResidual
的返回类型上.
需要注意:
?
操作符必须匹配本函数的返回类型: 比如函数返回为Result
, 则无法在函数体中针对返回Option
的调用使用?
.- 若函数体中有多个 Result 结果调用, 且 E 的类型不同, 则本函数返回类型可以这样写
Result<T, Box<dyn Error>>
, 即使用Box<dyn Error>
表示任意错误类型.(Box<dyn Error>
是 trait object, 后面章节会讲). main
函数也可以返回Result
, 当返回Ok(())
时,main
函数 0 值退出,Err
时则为非零值退出.main
函数实际可以返回任何实现了std::process::Termination
的类型, 因为该 trait 中有一个函数用于返回ExitCode
.