Programmingシステムプログラムエンジニア / Rustシニアデベロッパー

Unsafe Rustとは何か、なぜ必要なのか、そのルールは何か、リスクを最小限に抑えるためにunsafeブロックを正しく使用する方法は何か?

Hintsage AIアシスタントで面接を突破

答え

Unsafe Rustは、安全なRustのサブセットを拡張したもので、コンパイラが所有権、ライフタイム、およびエイリアシングの整合性を確認できない操作を使用できるようにします。主な使用例は、低レベルライブラリとの相互作用、FFI、手動メモリ管理、安全なRustモデルに収まらない抽象化の実装です。

unsafeの主な特徴:

  • 使用するにはブロックを明示的に宣言する必要があります: unsafe { ... } または関数: unsafe fn some_func()
  • unsafeブロック内で許可されている危険な操作: rawポインタの逆参照、安全でない関数やメソッドの呼び出し、unionへのアクセス、静的ミュータブル変数、非構造化メモリのメソッドの実装
  • unsafeの使用はブロック内の全コードを言語全体に対して危険なものにするわけではありません — 手動で正当性を保証する必要があります。

例:

let x: i32 = 10; let ptr: *const i32 = &x as *const i32; unsafe { println!("Value: {}", *ptr); // rawポインタの逆参照 }

問題のある質問

unsafeブロック内のコードは完全に危険であり、コンパイラによってチェックされないのか、それともコンパイラは依然としてborrowing checkerやその他のチェックを適用するのか?

答え: いいえ、unsafeブロック内では、コンパイラはRustの多くのルール(例えば、型、所有権のルール、構文)を引き続きチェックしますが、他の方法では許可されない操作のみを実行できます。borrow checkerを完全に無効化することはできません!

例:

let mut x = 0; let r1 = &mut x as *mut i32; // 禁止されている: let r2 = &mut x as *mut i32; // unsafe内でも、ミュータビリティが守られない場合はエラーが発生します。

このテーマの詳細を知らないことによる実際のエラーの例


物語

非同期ファイルライブラリでは、バッファのコントロールにrawポインタを使用しましたが、バッファのライフタイムを正しく追跡することを忘れました。その結果、ファイルを閉じるとポインタが「ダングリング」になり、アクセスが未定義の動作を引き起こしました(unsafeセクションはuse-after-freeから救ってくれませんでした)。


物語

独自のVecのような構造の実装では、プログラマーがunsafeを介して手動でバッファを拡張しました。オフセットのエラーにより、要素の不正なコピーとデータの破損が発生しました。これは、アラインメントと型のレイアウトが考慮されていなかったためです。


物語

スレッドのディスクリプタでは、適切な同期なしに静的ミュータブルポインタ(static mut)が使用されました。そのため、マルチスレッドモードで偶発的にデータレースが発生し、アプリケーションの sporadic crash を引き起こし、fuzzingによってのみ捕捉されました。