未定義とみなされる動作

Rust コードは以下のリスト内のいずれかの動作を発現すると不正となる。これはアンセーフブロックとアンセーフ関数内にあるコードに含まれる。アンセーフは未定義動作を回避するのがプログラマである事を意味するだけだ; Rust プログラムが未定義動作を決して発生させてはならないという事実を何ら変えるものではない。

unsafe コードの記述時に、任意のセーフコードがその unsafe コードと相互作用してこれらの動作を誘発できないようにするのはプログラマの責任である。任意のセーフなクライアントに対してこの特性を満たす unsafe コードは健全であると呼ばれる; もしも unsafe コードがセーフコードにより誤用されうるなら、それは不健全である。

⚠︎ 警告

以下のリストは網羅的ではない; 増減がありうる。何が許され何が許されないかについての Rust の意味論の正式なモデルはないため、未定義とみなされる動作はもっとあるかもしれない。また私達はそのリストのいくつかを将来的に定義済とする権利も留保する。言い換えると、このリストは全ての将来の Rust のバージョンで何かが確実に常に未定義だとは言っていない (ただし将来的にリストのいくつかの項目について、そのような約束をする可能性はある)。

アンセーフコードを書く前に Rustonomicon を読んでほしい。

ⓘ 参考

未定義動作はプログラム全体に影響する。例えば、C での未定義動作を発現する C の関数の呼出はあなたのプログラム全体が未定義動作を含む事を意味し、それは Rust コードにも影響しうる。そして逆も同様、Rust での未定義動作は他へのあらゆる FFI 呼出により実行されるコードの悪影響の原因になりうる。

ポイントされるバイト列

ポインタや参照が “ポイントする” バイト列の区間はポインタの値とポイントされる型のサイズにより決定される (size_of_val の使用)。

ミスアライメントされたポインタ基準の場所

場所はもし場所計算中の最後の * 射影がその型にアライメントされていないポインタ上で実行されると、“ミスアライメントされたポインタ基準” であると言われる。(もし場所式の中に * 射影がなければ、これはローカルか静的なフィールドへのアクセスで rustc は正しいアライメントを生成する。もし複数の * 射影があれば、それらはそれぞれメモリからそれ自身を逆参照するためのポインタのロードを招き、これらのそれぞれのロードがアライメント制約の対象となる。表面的な Rust の構文では自動逆参照があるため特定の * 射影は省略できる点に注意せよ; 私達はここでは完全に展開された場所式について考えている。)

実例として、もし ptr*const S の型を持ち S のアライメントが 8 なら、ptr はアライメント 8 でなければならず、そうでなければ (*ptr).f は “ミスアライメントされたポインタ基準” になる。これはたとえフィールド f の型が u8 (つまりアライメント 1 の型) でも真実である。言い換えると、アライメントの要件はポインタが逆参照された型から導出され、アクセスされるフィールドの型からではない。

ミスアライメントされたポインタ基準の場所はそれがロードまたはストアされた時にのみ未定義動作につながる点に注意せよ。

&raw const / &raw mut はそのような場所でも使える。

& / &mut における場所はフィールド型のアライメントが要求され (そうでなければプログラムは “不正な値の生成” になるだろう)、これはアライメントされたポインタに基づく場合よりは一般に小さい制限の要求である。

フィールドの型がそれを含む型よりも大きなアライメントを持つかもしれない場合 (つまり、repr(packed))、参照をとる事はコンパイルエラーにつながる。これはつまりアライメントされたポインタを基準にすると常に十分に新しい参照のアライメントを保証できるが、必ずしもそれが必要ではない事を意味している。

ダングリングポインタ

参照/ポインタはもしそれがポイントするバイト列の全てが同一の生きた割り当ての一部でないと “ダングリング” になる (つまり具体的にはそれらは全て特定の割り当ての一部でなければならない)。

もしサイズが 0 なら、そのポインタは自明だが “ダングリング” は決してしない (たとえそれがヌルポインタでも)。

動的サイズ型 (スライスや文字列のような) はそれらの範囲全体をポイントするため、長さのメタデータが極端に大きくないのも重要である。

具体的には、Rust の値の動的サイズ (size_of_val により決定される) は一回の割り当てが isize::MAX より大きくなるのが不可能なため、決して isize::MAX を超えてはならない。

不正な値

Rust のコンパイラはプログラムの実行中に生成される全ての値が “妥当” であり、そして不正な値はそのためすぐに UB になると仮定する。

値が妥当かは型に依存する:

参考: 未初期化のメモリは制限された有効な値の集合を持つ任意の型において暗黙的に不正である。言い換えると、未初期化メモリの読取が許されるのは union の内側と “パディング” (型のフィールド間にある隙間) の中のみである。