サンプルコードで身につける: unsafe — コンパイラの保証を一部外す
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1生ポインタを作って読む
&value as *const i32 で生ポインタを「作る」のは普通のコードで書けて、* で「読む」操作だけが unsafe ブロックを要求します。有効な変数の参照から作った直後のポインタなので、この読み取りは安全に閉じています。まずは unsafe の境界線がどこに引かれているかを確認してください。
2*mut で書き換える
可変参照から *mut i32 を作れば、unsafe ブロックの中でポインタ越しの書き換えができます。借用チェッカーの管理外になるぶん、「ポインタが指す先が有効なまま使う」責任がプログラマに移ります。この例は有効な変数を直後に書き換えるだけなので、安全に閉じています。
3unsafe fn — 責任を呼び出し側へ
関数自体に unsafe を付けると、「引数のポインタが有効であることは呼び出し側が保証する」という契約になります。呼び出す側にも unsafe ブロックが必要になり、責任の所在がコードに明示されます。C ライブラリの関数を Rust から包むときによく見る形です。
4unsafe を安全な関数に閉じ込める
長さを検査してから unsafe で先頭要素を読む first は、外から見ればただの安全な関数です。「危険な操作を検査と一緒に内側へ閉じ込めて、安全な API だけを公開する」のが Rust 流の unsafe の使い方です。標準ライブラリの Vec なども内部はこの構造でできています。
5split_at_mut — 標準ライブラリの中の unsafe
1つの配列の前半と後半へ同時に &mut を取ることは、借用ルールの上では書けません。標準の split_at_mut は「重ならない2つの範囲だから安全」という事実を内部の unsafe で実装し、安全な API として提供しています。私たちは unsafe を書かずに、その恩恵だけを受けられます。