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

Паника

Паника (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 и убедиться, что в этом случае сообщение не выводится.