Skip to content

18.2 Panic 实现机制

在 Rust 中,Panic 的实现机制有两种方式:unwind 和 abort。

  • unwind 方式在发生 panic 的时候,会一层一层地退出函数调用栈,在此过程中,当前栈内的局部变量还可以正常析构。

  • abort 方式在发生 panic 的时候,会直接退出整个程序。

在常见的操作系统上,默认情况下,编译器使用的是 unwind 方式。所以在发生 panic 的时候,我们可以通过一层层地调用栈找到发生 panic 的第一现场,就像前面例子展示的那样。

但是,unwind 并不是在所有平台上都能获得良好支持的。在某些嵌入式系统上,unwind 根本无法实现,或者占用的资源太多。在这种时候,我们可以选择使用 abort 方式实现 panic。

编译器提供了一个选项,供用户指定 panic 的实现方式。如下所示:


rust
rustc -C panic=unwind test.rs
rustc -C panic=abort test.rs

读者可以试试上面两个编译命令,做一下对比。可以看到它们生成的代码,panic 时的行为是不一样的,生成的可执行程序大小也不同。

Rust 中,通过 unwind 方式实现的 panic,其内部的实现方式基本与 C++的异常是一样的。而且,Rust 提供了一些工具函数,可以让用户在代码中终止栈展开。示例如下:


rust
use std::panic;

fn main() {
    panic::catch_unwind(|| {
        let x : Option<i32> = None;
        x.unwrap();
        println!("interrupted.");
    }).ok();

    println!("continue to execute.");
}

编译执行可见,在 unwrap 语句后面的这条打印语句并未执行。因为在上一条语句中触发了 panic,这个函数调用栈开始销毁。但是我们有一句 catch_unwind 阻止了函数调用栈的继续展开,就像 C++里面的 try-catch 机制一样。因此,main 方法并没有继续被销毁,最后那条语句可以正常打印输出。

如果我们尝试使用“-C panic=abort”选项编译上面的代码,可以看到这个 catch_unwind 起不了什么作用,最后那条语句无法正常打印输出。

但是,请大家注意,这个 catch_unwind 机制绝对不是设计用于模拟“try-catch”机制的。请大家永远不要利用这个机制来做正常的流程控制。Rust 推荐的错误处理机制是用返回值,第 33 章讲解 Rust 的错误处理机制。panic 出现的场景一般是:如果继续执行下去就会有极其严重的内存安全问题,这种时候让程序继续执行导致的危害比崩溃更严重,此时 panic 就是最后的一种错误处理机制。它的主要用处参考下面的情况:

  • 在 FFI 场景下的时候,当 C 语言调用了 Rust 的函数,在 Rust 内部出现了 panic,如果这个 panic 在 Rust 内部没有处理好,直接扔到 C 代码中去,会导致 C 语言产生“未定义行为”(undefined behavior)。

  • 某些高级抽象机制需要阻止栈展开,比如线程池。如果一个线程中出现了 panic,我们希望只把这个线程关闭,而不至于将整个线程池“拖下水”。

Released under the MIT License