項目16:unsafeコードを書かないようにしよう
https://effective-rust.com/unsafe.html
unsafe コードは極力避けるべきであり、FFI や低レイヤ実装、実現したい機能が標準ライブラリや crates.io に無い場合を除いて使うべきではない
Rust の メモリ安全性 とそのコスト
Rust のメモリ安全性は、実行時のオーバーヘッド無しで保証される
この保証を得るために、Rust を書く際には 借用チェッカ に対応するコードを記述する必要があり、使用する 参照型 を正確に指定する必要がある
Unsafe Rust の概要
Unsafe Rust は、Rust の スーパーセット であり、いくつかの制約とそれに対する安全保障を緩和する
具体的には、ブロックの前に unsafe と書くことで、そのブロック内は unsafe モード実行されるようになる
このモードでは、通常の Rust では許可されていない操作が可能になる
e.g. C の ポインタ のような動作をする 生ポインタ の利用
生ポインタは 借用ルール の対象外であり、デリファレンス 時や有効なメモリを指していることを、プログラマが保証する必要がある
なぜ Unsafe Rust を避けるべきか
Rust に移行しても、結局 Rust 内で C のようなコードを書くなら意味がないため
しかし、unsafe コードが必要になるケースもある
e.g. 低レベル なライブラリのコードを書く場合や、他言語のコードと連携する必要がある場合
項目34:FFI境界を通過するものを制御しよう
「unsafe コードは書かないようにしよう」
この項目のタイトルの中で重要なのは「書く」という点
ほとんど場合、必要な unsafe なコードはすでに誰かが書いているため、自身で書く必要は上記を除いてほとんど無い
標準ライブラリにおける unsafe コード
標準ライブラリではたくさんの unsafe コードが含まれている
e.g.
スマートポインタ型(Rc や RefCell、Arc など)
unsafe なコード(特に生ポインタ)を内部的に用いて、それぞれ固有の セマンティクス をユーザに提供する
同期プリミティブ(Mutex や RwLock、それぞれに対応するガード)
unsafe な OS 固有のコードを内部的に用いている
std::pin::Pin: 値がメモリ上で移動しないことを強制する
これにより 自己参照データ構造(値が自身のアドレスを参照するデータ構造)が安全に動作する
https://rust-unofficial.github.io/too-many-lists/
std::sync::atomic: プリミティブ型 の アトミック なバージョンを提供する
アトミックとは、複数スレッドが同じ変数を操作しても データ競合 が起きないことを意味する
std::mem の様々な関数(take や swap、replace)
借用チェッカ の制約を回避しながら、メモリ内の値を操作する機能を提供する
これらの機能を正しく使うにはまだ注意が必要だが、unsafe コードはカプセル化されて隠蔽されているので、様々な問題が起きないようになっている
crates.io にも unsafe コードをカプセル化しているクレートがある
e.g.
once_cell: グローバル変数のようなものを 1 度だけ初期化する方法を提供する
rand: 乱数生成を提供
OS や CPU の低レベルや機能を利用
byteorder: 生の バイトデータ を数値に変換する機能を提供
cxx: Rust と C++ との相互運用を可能にする
unsafe が必要になる場合の対応
unsafe を使わざるを得ないケースもある
e.g. 他言語のコードと相互運用するために FFI を用いるケース
しかし、このような場合でも unsafe コードをラップする 層 を作り、その中に unsafe 操作を閉じ込める のが良い
これにより、問題を 局在化 することができる
unsafe コードを書く際の注意点
ドキュメントに安全性に関するコメントを書く
具体的には、unsafe コードが依存する 前提条件 と 不変条件 を書く
Clippy を用いると、これに関する警告を表示してくれる(項目29:Clippyに耳を傾けよう#67652c3c75d04f00007a4b11)
unsafe ブロックを最小化する
unsafe コードの範囲をできるだけ小さくすることで、失敗した場合の影響範囲を限定する
Lint 項目 unsafe_op_in_unsafe_fn を有効にすると、unsafe 関数の中であっても unsafe な操作を行う場合には、明示的に unsafe ブロックを使う必要がある
普段以上にテストを書く
項目30:ユニットテスト以上のものを書こう
コードを診断ツール(Miri)を使用する
Miri は コンパイラ の出力する IR を解釈するので、コンパイラが検出できない様々なエラーを検知できる
マルチスレッドでの使用は慎重に考える
特に 共有状態 がある場合
項目17:状態共有並列実行には気を付けよう
#Rust #Effective_Rust_―_Rustコードを改善し、エコシステムを最大限に活用するための35項目