恐慌
恐慌是 Rust 语言的核心部分。内置操作(如索引)在运行时检查内存安全。当尝试越界索引时,会导致恐慌。
在标准库中,恐慌具有定义的行为:它会展开恐慌线程的堆栈,除非用户选择在恐慌时中止程序。
然而,在没有标准库的程序中,恐慌行为是未定义的。可以通过声明一个 #[panic_handler]
函数来选择行为。此函数必须在程序的依赖关系图中恰好出现 一次,并且必须具有以下签名:fn(&PanicInfo) -> !
,其中 PanicInfo
是一个包含有关恐慌位置信息的结构体。
鉴于嵌入式系统从面向用户到安全关键(不能崩溃)不等,没有一种通用的恐慌行为,但有很多常用的行为。这些常见行为已被打包到定义 #[panic_handler]
函数的板条箱中。一些例子包括
panic-abort
。恐慌会导致执行中止指令。panic-halt
。恐慌会导致程序或当前线程通过进入无限循环而停止。panic-itm
。恐慌消息使用 ITM(ARM Cortex-M 特定的外设)进行记录。panic-semihosting
。恐慌消息使用半主机技术记录到主机。
您可能能够在 crates.io 上搜索 panic-handler
关键字找到更多板条箱。
程序可以通过简单地链接到相应的板条箱来选择其中一种行为。恐慌行为在应用程序源代码中以单行代码的形式表达,这不仅对文档很有用,而且还可以用于根据编译配置文件更改恐慌行为。例如
#![no_main]
#![no_std]
// dev profile: easier to debug panics; can put a breakpoint on `rust_begin_unwind`
#[cfg(debug_assertions)]
use panic_halt as _;
// release profile: minimize the binary size of the application
#[cfg(not(debug_assertions))]
use panic_abort as _;
// ..
在此示例中,当使用开发配置文件(cargo build
)构建时,板条箱链接到 panic-halt
板条箱,但当使用发布配置文件(cargo build --release
)构建时,链接到 panic-abort
板条箱。
use panic_abort as _;
形式的use
语句用于确保panic_abort
恐慌处理程序包含在我们的最终可执行文件中,同时向编译器明确表示我们不会显式使用板条箱中的任何内容。如果没有as _
重命名,编译器会警告我们有一个未使用的导入。有时您可能会看到extern crate panic_abort
,这是一种旧的风格,在 Rust 的 2018 版之前使用,现在应该只用于“系统根”板条箱(与 Rust 本身一起分发的板条箱),例如proc_macro
、alloc
、std
和test
。
一个例子
以下是一个尝试索引数组超出其长度的示例。该操作会导致恐慌。
#![no_main]
#![no_std]
use panic_semihosting as _;
use cortex_m_rt::entry;
#[entry]
fn main() -> ! {
let xs = [0, 1, 2];
let i = xs.len();
let _y = xs[i]; // out of bounds access
loop {}
}
此示例选择了 panic-semihosting
行为,该行为使用半主机将恐慌消息打印到主机控制台。
$ cargo run
Running `qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb (..)
panicked at 'index out of bounds: the len is 3 but the index is 4', src/main.rs:12:13
您可以尝试将行为更改为 panic-halt
,并确认在这种情况下不会打印任何消息。