In Panik geraten
Das Auslösen einer Panik ist ein wesentlicher Bestandteil der Sprache Rust. Eingebaute Operationen wie der Zugriff per Index werden zur Laufzeit auf Speichersicherheit überprüft. Erfolgt ein Zugriff außerhalb der zulässigen Grenzen, führt dies zu einer Panik.
In der Standardbibliothek ist das Verhalten bei einer Panik definiert: Der Stack des betroffenen Threads wird abgewickelt (Stack Unwinding), es sei denn, der Benutzer hat sich für einen Programmabbruch im Falle einer Panik entschieden.
In Programmen ohne Standardbibliothek ist das Verhalten bei einer Panik hingegen nicht definiert. Ein Verhalten lässt sich durch die Deklaration einer #[panic_handler]-Funktion festlegen. Diese Funktion muss im Abhängigkeitsgraphen des Programms genau einmal vorkommen und folgende Signatur aufweisen: fn(&PanicInfo) -> !, wobei PanicInfo eine Struktur ist, die Informationen über den Ort des Panic enthält.
Da das Spektrum eingebetteter Systeme von anwenderorientierten bis hin zu sicherheitskritischen Anwendungen (bei denen ein Absturz ausgeschlossen sein muss) reicht, gibt es kein universelles Verhalten im Fehlerfall („Panic“); es existieren jedoch zahlreiche gängige Ansätze. Diese verbreiteten Verhaltensweisen wurden in Crates zusammengefasst, die die Funktion #[panic_handler] definieren. Zu den Beispielen gehören:
panic-abort. Eine Panic bewirkt die Ausführung der Abbruchanweisung.panic-halt. Ein Panic bewirkt, dass das Programm oder der aktuelle Thread anhält, indem es bzw. er in eine Endlosschleife eintritt.panic-itm. Die Panik-Meldung wird mithilfe des ITM protokolliert – einer für ARM Cortex-M spezifischen Peripheriekomponente.panic-semihosting. Die Panik-Meldung wird mithilfe der Semihosting-Technik auf dem Host protokolliert.
Möglicherweise findest du noch mehr Crates, wenn du auf crates.io nach dem Schlüsselwort panic-handler suchst.
Ein Programm kann eines dieser Verhalten einfach dadurch auswählen, dass es das entsprechende Crate einbindet. Dass das Verhalten im Fehlerfall (Panic-Verhalten) im Quellcode einer Anwendung als einzelne Codezeile ausgedrückt wird, ist nicht nur als Dokumentation nützlich, sondern ermöglicht es auch, dieses Verhalten je nach Kompilierungsprofil anzupassen. Zum Beispiel:
#![no_main]
#![no_std]
// dev-Profil: einfacheres Debuggen von Panics; man kann einen Breakpoint bei
// `rust_begin_unwind` setzen.
#[cfg(debug_assertions)]
use panic_halt as _;
// Release-Profil: Minimierung der Binaergroesse der Anwendung
#[cfg(not(debug_assertions))]
use panic_abort as _;
// ..
In diesem Beispiel verlinkt der Crate beim Bauen mit dem Dev-Profil (cargo build) auf den panic-halt-Crate, beim Bauen mit dem Release-Profil (cargo build --release) hingegen auf den panic-abort-Crate.
Dieuse panic_abort as _;-Variante deruse-Anweisung wird verwendet, um sicherzustellen, dass derpanic_abort-Panic-Handler in die fertige ausführbare Datei aufgenommen wird, während dem Compiler gleichzeitig signalisiert wird, dass wir nichts aus diesem Crate explizit verwenden werden. Ohne die Umbenennung mittelsas _würde der Compiler eine Warnung wegen eines ungenutzten Imports ausgeben. Gelegentlich stößt man stattdessen aufextern crate panic_abort; dabei handelt es sich um einen älteren Stil, der vor der Rust-Edition 2018 üblich war und heute nur noch für sogenannte „sysroot“-Crates (die gemeinsam mit Rust selbst ausgeliefert werden) – wie etwaproc_macro,alloc,stdundtest– verwendet werden sollte.
Ein Beispiel
Hier ist ein Beispiel, das versucht, auf ein Array an einer Position zuzugreifen, die über dessen Länge hinausgeht. Der Vorgang führt zu einer Panic.
#![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]; // Zugriff ausserhalb der zulaessigen Grenzen
loop {}
}
Für dieses Beispiel wurde das Verhalten panic-semihosting gewählt, das die Panic-Meldung mittels Semihosting auf der Host-Konsole ausgibt.
$ 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:13Sie können versuchen, das Verhalten auf panic-halt zu ändern, und bestätigen, dass in diesem Fall keine Meldung ausgegeben wird.