パニック

パニックは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]

// dev profile: easier to debug panics; can put a breakpoint on `rust_begin_unwind`
// 開発プロファイル:パニックのデバッグを容易にします。`rust_begin_unwind`にブレイクポイントを置くことを可能にします。
#[cfg(debug_assertions)]
extern crate panic_halt;

// release profile: minimize the binary size of the application
// リリースプロファイル:アプリケーションのバイナリサイズを最小化します。
#[cfg(not(debug_assertions))]
extern crate panic_abort;

// ..

この例では、開発プロファイルでビルド(cargo build)した時は、panic-haltクレートとリンクします。 しかし、リリースプロファイルでビルド(cargo build --release)した時は、panic-abortクレートとリンクします。

配列の長さを超えてアクセスしようとする例を示します。この操作はパニックを引き起こします。

#![no_main]
#![no_std]

extern crate panic_semihosting;

use cortex_m_rt::entry;

#[entry]
fn main() -> ! {
    let xs = [0, 1, 2];
    let i = xs.len() + 1;
//    let _y = xs[i]; // out of bounds access
    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に変更し、その場合にメッセージが出力されないことを確認することができます。