Rustでunsafeが必要な操作
Rustでは unsafe
の表明を行わない限り未定義動作が発生しない。コンパイラや言語仕様のミスにより未定義動作が発生しうる場合には優先的に修正される。(なお、整数オーバーフローのように特定条件下でエラーになるものであっても、正しくunwind/abortできるものは未定義動作とは呼ばない。)
unsafe
の表明
unsafe
の表明は、構文的には以下のいずれかである。
unsafe
ブロックunsafe impl
unsafe
を表明した場合、その部分では「安全かどうかをコンパイラが保証できない操作」が行えるため、この部分が安全であるかどうかを検査するのはプログラマの責任であるということになる。それぞれの操作には、それが安全に実行できるための事前条件(safety precondition)が決められているから、これが成り立っていることをプログラマが自分で保証する、という算段である。
unsafe
の要求
unsafe
を用いたライブラリ関数の場合、「関数が何らかの性質を満たしているときは安全」という状況が考えられる。例えば、比較関数が正当であることを前提としたソートアルゴリズムの実装というのが考えられる。
Rustでは原則としてこれは許されない。ライブラリの呼び出し側が (unsafe
だったり、可視性を破壊したりしていない範囲内で) どんな異常な引数で関数を呼んでも、その関数は安全に動作しないといけない。
どうしても呼び出し側に安全性の責任を転嫁したい場合は、関数シグネチャに unsafe
をつける。これにより、そのライブラリ関数は、安全に使うための追加の事前条件(safety precondition)を要求していることになる。その事前条件はドキュメントに説明されるべきということになる。
このように unsafe
を要求する構文には以下のものがある。
- 関数シグネチャの
unsafe
unsafe trait
なお、関数定義の unsafe
は要求と表明を兼ねている。
unsafe
ブロック/関数内でないとできない操作の一覧
unsafeが適切に呼ばれているかどうかは rustc::middle::effect
モジュール で検査されている。
以下では事前条件も書いてみるが、すべて筆者による推測である。
unsafe
関数/unsafe
メソッドの呼び出し
unsafe
のついている関数やメソッドを呼び出したときに発生する。関数を取り出して呼び出していない場合は発生しない。
事前条件: その関数/メソッドによって指定されている事前条件を守る。
生ポインタの参照外し
*const T
や *mut T
型の値 p
に対し、 *p
を行う。 &*p
のように左辺値の場合 (生ポインタを参照に昇格するのに使う) にも unsafe
が必要である。
事前条件・不変条件: おそらく以下のような条件が必要とされている。
- アラインメントが揃っている。
- 有効な場所を指している。
- 有効な値が格納されている。または、昇格した参照が使用されるまでに有効な値が格納される。
- エイリアスを持たないか、これ自身を含む全てのエイリアスが読み取り専用として扱われている。
インラインアセンブリ
asm!()
や global_asm!()
によるインラインアセンブリの埋め込みは常に unsafe
である。
事前条件・不変条件: Rustのもつ全ての不変条件を守ること。例えば、書き込み借用できるメモリ以外に書き込まない。不正な値を書き込まない。 Copy
でない値をコピーした場合に元の位置のデストラクタを呼んではいけない。など。
static mut
変数へのアクセス
static mut
で宣言された静的変数の読み取り/書き込みアクセスは unsafe
である。
事前条件・不変条件: 書き込み参照するときは、他に誰かが参照していないこと。読み取り参照するときは、他に誰かが書き込み参照していないこと。(他スレッドからのアクセスも含む)
extern { static }
変数へのアクセス
extern { static X : u32; }
のように、FFIで外部の静的変数を参照する変数への読み取り/書き込みアクセスは unsafe
である。
事前条件: 値を取り出す場合は、不正な値が入っていないよう注意する。
なお、この条件は互換性のために現在は警告扱いになっている(warning cycle)。将来はエラーとなる予定である。
union
要素へのアクセス
union
要素へのアクセス (読み取り、代入、パターンマッチによる読み取り) は unsafe
である。
事前条件・不変条件:
- 読み取りでは、そのフィールドに有効な値が入っていること。
- 書き込みでは、この
union
の何らかの不変条件を保ち、結果的にDrop
が正常に動作すること。 (Drop
を実装していなければ問題ない)
なお、最近の変更により、 Copy
な union
の要素への代入は unsafe
ではなくなった。