Skip to main content

C9 错误处理

Rust 有两类错误, 包括可恢复, 不可恢复两种. 分别可用 panic!Result<T, E> 表示.

https://doc.rust-lang.org/book/ch09-00-error-handling.html

在 Rust 中没有 exception 的概念, 而是通过上述两种类型结合实现 exception.

不可恢复错误 panic

在 Rust 中, 有两种 panic 的可能性:

  1. 在代码中触发 panic(比如数组下标访问越界)
  2. 主动使用 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 的返回类型上.

需要注意:

  1. ? 操作符必须匹配本函数的返回类型: 比如函数返回为 Result, 则无法在函数体中针对返回 Option 的调用使用 ?.
  2. 若函数体中有多个 Result 结果调用, 且 E 的类型不同, 则本函数返回类型可以这样写 Result<T, Box<dyn Error>>, 即使用 Box<dyn Error> 表示任意错误类型.(Box<dyn Error> 是 trait object, 后面章节会讲).
  3. main 函数也可以返回 Result, 当返回 Ok(()) 时, main 函数 0 值退出, Err 时则为非零值退出.
  4. main 函数实际可以返回任何实现了 std::process::Termination 的类型, 因为该 trait 中有一个函数用于返回 ExitCode.