Паника
Паника (panic) — важная часть языка Rust. Встроенные операции, например индексация, проверяются во время выполнения, чтобы гарантировать безопасность памяти. Если выполняется попытка индексации за пределами массива, происходит паника.
В стандартной библиотеке поведение при панике определено: она разматывает стек паникующего потока (unwinding), если только пользователь не выбрал прерывание программы при панике.
Однако в программах без стандартной библиотеки поведение при панике не определено. Его можно выбрать, объявив функцию #[panic_handler]. Эта функция должна встречаться в графе зависимостей программы ровно один раз и иметь такую сигнатуру: fn(&PanicInfo) -> !, где PanicInfo — структура, которая содержит информацию о месте возникновения паники.
Поскольку встраиваемые системы бывают самыми разными — от пользовательских устройств до систем, критичных к безопасности (которые не должны падать), — универсального поведения при панике не существует, но есть множество распространённых вариантов. Эти распространённые варианты упакованы в крейты, которые определяют функцию #[panic_handler]. Вот некоторые примеры:
panic-abort. При панике выполняется инструкция abort.panic-halt. При панике программа или текущий поток останавливается, входя в бесконечный цикл.panic-itm. Сообщение о панике записывается через ITM — периферию, специфичную для ARM Cortex-M.panic-semihosting. Сообщение о панике передаётся хосту с помощью техники semihosting.
Ещё больше крейтов можно найти на crates.io, выполнив поиск по ключевому слову panic-handler.
Программа выбирает одно из этих поведений, просто подключая соответствующий крейт. Тот факт, что поведение при панике задаётся в исходном коде приложения одной строкой, полезен не только как документация, но и позволяет менять поведение при панике в зависимости от профиля сборки. Например:
#![no_main]
#![no_std]
// профиль dev: удобнее отлаживать паники; можно поставить точку останова на `rust_begin_unwind`
#[cfg(debug_assertions)]
use panic_halt as _;
// профиль release: минимизировать размер бинарного файла приложения
#[cfg(not(debug_assertions))]
use panic_abort as _;
// ..
В этом примере подключается крейт panic-halt, если сборка выполняется с профилем dev (cargo build), и panic-abort, если с профилем release (cargo build --release).
Форма
use panic_abort as _;используется, чтобы обработчик паникиpanic_abortпопал в итоговый исполняемый файл, а компилятору было понятно, что мы не будем явно использовать ничего из этого крейта. Без переименованияas _компилятор выдал бы предупреждение о неиспользуемом импорте. Иногда вместо этого встречаетсяextern crate panic_abort— более старый стиль, который использовался до издания Rust 2018 и теперь нужен только для «sysroot»-крейтов (поставляемых вместе с самим 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]; // доступ за границами
loop {}
}
В этом примере выбрано поведение panic-semihosting, которое выводит сообщение о панике в консоль хоста с помощью 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 и убедиться, что в этом случае сообщение не выводится.