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

パニック

パニックは Rust 言語の中核的な要素です。インデックスアクセスのような組み込み 演算は、メモリ安全性のために実行時チェックされます。範囲外のインデックスアクセスを試みると、 パニックが発生します。

標準ライブラリでは、パニックには定義された振る舞いがあります。パニックした スレッドのスタックをアンワインドします。ただし、ユーザーがパニック時にプログラムを アボートすることを選択している場合は除きます。

しかし、標準ライブラリのないプログラムでは、パニック時の振る舞いは未定義のままです。 #[panic_handler] 関数を宣言することで、振る舞いを選択できます。 この関数は、プログラムの依存グラフ内に必ず 一度だけ 現れなければならず、 シグネチャは次のとおりでなければなりません: fn(&PanicInfo) -> !。ここで PanicInfo は、パニックが発生した場所に関する情報を含む構造体です。

組み込みシステムは、ユーザー向けのものから安全性が重要なもの(クラッシュできない) まで幅広いため、すべてに当てはまる単一のパニック時の振る舞いはありませんが、 一般的によく使われる振る舞いは数多くあります。これらの一般的な振る舞いは、 #[panic_handler] 関数を定義するクレートとしてパッケージ化されています。例として、 次のようなものがあります:

  • panic-abort. パニックが発生すると、アボート命令が実行されます。
  • panic-halt. パニックが発生すると、プログラム、または現在のスレッドは、 無限ループに入ることで停止します。
  • panic-itm. パニックメッセージは、ARM Cortex-M 固有の周辺機能である ITM を 使ってログに記録されます。
  • panic-semihosting. パニックメッセージは、セミホスティングを使ってホストに ログ出力されます。

crates.io で panic-handler キーワードを検索すると、さらに多くのクレートが 見つかるかもしれません。

プログラムは、対応するクレートをリンクするだけで、これらの振る舞いのうち 1 つを選択できます。パニック時の振る舞いがアプリケーションのソース中で 1 行のコードとして表現されるという事実は、ドキュメントとして有用なだけでなく、 コンパイルプロファイルに応じてパニック時の振る舞いを変更するためにも利用できます。 たとえば、次のようになります:

#![no_main]
#![no_std]

// dev プロファイル: パニックをデバッグしやすい; `rust_begin_unwind` にブレークポイントを設定できる
#[cfg(debug_assertions)]
use panic_halt as _;

// release プロファイル: アプリケーションのバイナリサイズを最小限に抑える
#[cfg(not(debug_assertions))]
use panic_abort as _;

// ..

この例では、dev プロファイル(cargo build)でビルドした場合は panic-halt クレートにリンクしますが、release プロファイル (cargo build --release)でビルドした場合は panic-abort クレートにリンクします。

use 文の use panic_abort as _; という形式は、panic_abort のパニックハンドラが 最終的な実行可能ファイルに含まれるようにしつつ、そのクレートから何かを明示的に使うことはないと コンパイラに明確に伝えるために使われます。as _ によるリネームがないと、 コンパイラは未使用のインポートがあると警告します。 代わりに extern crate panic_abort を見かけることもありますが、これは Rust の 2018 edition より前に使われていた古い書き方であり、現在では proc_macroallocstdtest のような「sysroot」クレート(Rust 自体とともに配布されるもの)に対してのみ 使うべきです。

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

#![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 に変更して、その場合はメッセージが 表示されないことを確認してみてください。