パニック
パニックはRustのコア部分です。インデックス操作のような言語組込みの操作は、メモリ安全性をランタイム時に検査されます。 範囲外のインデックスにアクセスしようとすると、パニックが発生します。
標準ライブラリでは、パニックは定義された動作です。ユーザがパニック発生時にプログラムをアボートする選択をしない限り、 パニックを起こしたスレッドのスタックを巻き戻します。
しかし、非標準のプログラムでは、パニック時の挙動は、未定義のままです。#[panic_handler]関数を宣言することにより、 挙動を選択することができます。この関数は、プログラムの依存関係グラフに、1回だけ現れる必要があります。 そして、 fn(&PanicInfo) -> !のシグネチャを持つ必要があります。 ここで、PanicInfoは、パニックした位置情報を含む構造体です。
組込みシステムは、ユーザとやり取りするものから、安全性が重要な(クラッシュできない)ものまであります。 そのため、全てのパニック時動作に対応できる唯一のものはありませんが、よく利用される挙動がたくさんあります。 これらの一般的な挙動が、#[panic_handler]関数を定義するクレートにまとめられています。 いくつか、例を挙げます。
panic-abort。パニックが発生すると、アボート命令を実行します。panic-halt。パニックが発生すると、プログラム、または、現在のスレッドは、無限ループに入ることで停止します。panic-itm。パニック発生時のメッセージは、ARM Cortex-M固有のペリフェラルであるITMを使ってログ出力されます。panic-semihosting。パニック発生時のメッセージは、セミホスティングを使ってログ出力されます。
crates.ioでpanic-handlerをキーワードに検索することで、さらにクレートを見つけることができます。
プログラムは、対応するクレートとリンクすることで、これらの挙動の中から1つを選びます。 パニック時の挙動がアプリケーションソースコードの中で単一行で表現されていることは、ドキュメントとして有用なだけでなく、 パニック時の挙動をコンパイル時のプロファイルで変更にする時にも利用できます。 例えば
#![no_main]
#![no_std]
// 開発プロファイル:パニックのデバッグを容易にします。`rust_begin_unwind`にブレイクポイントを置くことを可能にします。
#[cfg(debug_assertions)]
use panic_halt as _;
// リリースプロファイル:アプリケーションのバイナリサイズを最小化します。
#[cfg(not(debug_assertions))]
use panic_abort as _;
// ..
翻訳が古くなっています この例では、開発プロファイルでビルド(cargo build)した時は、panic-haltクレートとリンクします。 しかし、リリースプロファイルでビルド(cargo build --release)した時は、panic-abortクレートとリンクします。
Theuse panic_abort as _;form of theusestatement is used to ensure thepanic_abortpanic handler is included in our final executable while making it clear to the compiler that we won’t explicitly use anything from the crate. Without theas _rename, the compiler would warn that we have an unused import. Sometimes you might seeextern crate panic_abortinstead, which is an older style used before the 2018 edition of Rust, and should now only be used for “sysroot” crates (those distributed with Rust itself) such asproc_macro,alloc,std, andtest.
例
配列の長さを超えてアクセスしようとする例を示します。この操作はパニックを引き起こします。
#![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の挙動を選択しており、パニックメッセージは、 セミホスティングを使ってホストコンソールに出力されます。
$ 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に変更し、その場合にメッセージが出力されないことを確認することができます。