Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

9.1 不可恢复的错误以及panic!

9.1.1 Rust错误处理概述

Rust拥有极高的可靠性,而这种可靠性也延伸到了错误处理。在大部分情况下,Rust会迫使你意识到可能会出现错误的地方,然后在编译阶段就确保它们获得妥善的处理。

在Rust里,错误被分为两大类:

  • 可恢复的错误:比如说文件未找到。这种情况下,可以把错误信息传递给用户,并让用户再次尝试。
  • 不可恢复的错误:bug的另外一种说法,比如说索引越界。

其他大部分编程语言都没有刻意地区分这两种错误。它们通常通过异常这种单一机制来统一处理。但Rust里没有类似异常的机制。

  • 针对可恢复的错误,Rust提供了Result<T, E>这个类型,9.2. Result枚举与可恢复的错误 Pt.1会讲。
  • 针对不可恢复的错误,Rust提供了panic!这个宏。当执行这个宏时,程序会中止执行。

9.1.2 panic!

有时候在代码里会发生糟糕的问题,而针对这些问题开发者几乎束手无策。为了应对这种情况,Rust提供了panic!这个宏。

当这个宏执行时,会发生这些动作:

  • 它会打印出错误信息。
  • 然后展开(unwind)和清理调用栈。
  • 退出程序。

9.1.3 当panic!时:展开还是终止

展开调用栈时工作量极大,因为Rust会沿着调用栈往回走,清理沿途遇到的每个函数中的数据。

与之相反,Rust也提供了终止(abort)的选择。这种做法就是不进行清理,直接停止程序,程序所使用的内存稍后交给操作系统来清理。

如果你想要二进制文件更小,就把设置从“展开”改为“终止”:在Cargo.toml中适当的profile部分设置panic = "abort"

以我的Cargo.toml为例:

[package]
name = "RustStudy"
version = "0.1.0"
edition = "2021"

[dependencies]
rand = "0.8.5"

[profile.release]
panic = "abort"

profile.release的意思是在发布模式下运行时。

9.1.4 panic!

看一个panic!宏的例子:

fn main() {
    panic!("Something went wrong");
}

非常简单的例子。panic!宏的参数就是错误信息,它会在程序停止时被打印出来。

输出:

thread 'main' panicked at src/main.rs:2:5:
Something went wrong
stack backtrace:
   0: __rustc::rust_begin_unwind
             at /rustc/ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96/library/std/src/panicking.rs:689:5
   1: core::panicking::panic_fmt
             at /rustc/ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96/library/core/src/panicking.rs:80:14
   2: RustStudy::main
             at ./src/main.rs:2:5
   3: core::ops::function::FnOnce::call_once
             at /Users/stanyin/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

在前面几篇文章中也有程序恐慌的时候,但是那时候我没有把stack backtrace粘贴进文章,因为当时还没有讲到这里。上面呈现的才是完整的恐慌信息。现在我们来解析一下:

  • 第一行告诉了程序恐慌的位置——在src目录下的main.rs里的第2行第5列。
  • 第二行是程序定义的错误信息。
  • 从第三行开始的stack backtrace指的是回溯信息。在标记为2的位置就是main.rs。回溯信息里包含了到达发生错误的地点的所有被调用函数的列表,而在它下边——也就是标记为3的地方——就是调用了我们代码的代码,可能包含了Rust的核心库、标准库,抑或是第三方的库。
  • 最后一行的note说可以把RUST_BACKTRACE设为full来获得所有的详细信息。在Windows上,在终端输入set RUST_BACKTRACE=full && cargo run。在macOS/Linux上,输入export RUST_BACKTRACE=full && cargo run

为了获得像刚才这样的调试信息,还有一点前提条件:程序必须运行在调试模式(debug)下,而不是发布模式(--release)。cargo buildcargo run都是默认调试模式,所以只要确保不带--release这个flag就行。