Unsafe Rustを扱う
Rustの安全性チェックを回避する`unsafe`ブロックをいつ、どのように使うかを理解し、FFIや低レベルのメモリ操作を可能にします。
「Unsafe Rustを扱う」はCoddyKit上の無料Learn Rust Codingレッスンです。 これはレッスン3/3です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLearn Rust Coding学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Learn Rust Codingコースには全3レッスンが含まれています。
Unsafe Rust:はじめに
Unsafe Rustへようこそ。Rustはメモリ安全性の保証で知られていますが、こうしたチェックの一部を回避する必要がある特定の状況も存在します。
Rustの unsafe キーワードを使うと、それが可能になります。これは「悪い」コードを書くためのものではなく、メモリやハードウェアをより直接的に制御する必要がある高度な用途のためのツールです。
5つのUnsafeの特別な操作
unsafe ブロックを使用すると、通常はRustコンパイラーが禁止している5つの特別な操作が可能になります。
- 生ポインターをデリファレンスする:メモリアドレスに直接アクセスします。
unsafe関数やメソッドを呼び出す:コンパイラーが検証できない事前条件を持つ関数を実行します。unsafeトレイトを実装する:型が特定の不変条件を満たすことを宣言します。- 可変なstatic変数にアクセスまたは変更する:グローバルに可変状態を共有しますが、データ競合の危険があります。
unionのフィールドにアクセスする:現在のデータに対して無効な型である可能性のあるunionから値を読み取ります。
生ポインターのデリファレンス
生ポインターは、Rustが通常提供する安全性の保証を持たないメモリアドレスです。参照(&T、&mut T)から作成できますが、デリファレンスには unsafe ブロックが必要です。
つまり、ポインターが有効で、確保済みのメモリを指していることをプログラマーが保証する責任を負います。
fn main() {
let mut num = 5;
let r1 = &num as *const i32; // Immutable raw pointer
let r2 = &mut num as *mut i32; // Mutable raw pointer
unsafe { // The `unsafe` block starts here
println!("r1 points to: {}", *r1);
*r2 = 10; // Modify data through mutable raw pointer
println!("r2 points to: {}", *r2);
} // The `unsafe` block ends here
println!("Num is now: {}", num);
}Unsafe関数の呼び出し
unsafe fn としてマークされた関数があります。これは、その関数がRustコンパイラーでは保証できない事前条件を持つことを意味します。たとえば、有効なメモリアドレスを想定していても、それを検証できない場合があります。
このような関数の呼び出しは unsafe ブロックで囲む必要があります。これにより、呼び出し側がすべての事前条件を満たしていることを保証する、と明示します。
unsafe fn dangerous_operation() {
println!("This operation could be dangerous if preconditions aren't met!");
}
fn main() {
println!("Attempting a dangerous operation...");
unsafe { // Calling an unsafe function requires `unsafe`
dangerous_operation();
}
println!("Operation completed.");
}可変なstatic変数
Rustはデータ競合を避けるため、デフォルトではグローバルな可変状態を禁止します。ただし、static mut を使って可変なstatic変数を宣言できます。
これらの変数へのアクセスや変更は unsafe とみなされます。複数のスレッドが同時にアクセスし、未定義動作を引き起こす可能性があるためです。適切な同期はプログラマーの責任です。
static mut COUNTER: i32 = 0; // A mutable static variable
fn add_to_counter(inc: i32) {
unsafe { // Modifying `static mut` requires `unsafe`
COUNTER += inc;
}
}
fn main() {
add_to_counter(5);
unsafe { // Reading `static mut` also requires `unsafe`
println!("COUNTER after first add: {}", COUNTER);
}
add_to_counter(10);
unsafe {
println!("COUNTER after second add: {}", COUNTER);
}
}Unsafeトレイトの実装
Rustの一部のトレイトには unsafe trait のマークが付いています。これは、実装時にコンパイラーが検査できない特定の不変条件を満たす必要があることを示します。
一般的な例として、スレッド安全性に関係する Send と Sync トレイトがあります。unsafe trait を手動で実装する場合は、unsafe impl Trait for Type を使用し、安全性に関する条件を保証する必要があります。
Rustが自動的に、または安全な抽象化を通じて処理することが多いため、初心者がこれに遭遇する機会は比較的少ないでしょう。
Unionフィールドへのアクセス
unionは、いずれか1つのバリアントの値を保持できる特殊な型です。ただし、すべてのバリアントが同じメモリ領域を共有します。Cのunionに似ています。
union のフィールドにアクセスするには unsafe ブロックが必要です。コンパイラーは現在どのフィールドが有効かを判断できないためです。無効なフィールドから読み取ると、未定義動作やデータの誤解釈につながる可能性があります。
union Data {
integer: u32,
float: f32,
}
fn main() {
let mut d = Data { integer: 42 }; // Initialize with an integer
unsafe {
// Accessing 'integer' is safe here
println!("Integer value: {}", d.integer);
// Accessing 'float' is unsafe, as 'integer' was initialized.
// This would interpret the integer's bits as a float.
// println!("Float value (unsafe): {}", d.float);
}
d.float = 3.14; // Now initialize with a float
unsafe {
// Accessing 'float' is safe now
println!("Float value: {}", d.float);
}
}Unsafeが必要となる実践的な場面
unsafe は控えめに使用すべきですが、いくつかの高度なプログラミング作業では不可欠です。
- Foreign Function Interface(FFI):他の言語で書かれたコード(C/C++ライブラリなど)と連携します。
- パフォーマンス最適化:重要な処理では、Rustのチェックを回避することでわずかな性能向上が得られる場合があります(ただし、まず測定してください)。
- 低レベルのシステムプログラミング:オペレーティングシステム、デバイスドライバー、組み込みシステムなどを作成します。
- 安全な抽象化の構築:unsafeなプリミティブの上に、安全なデータ構造やAPIを実装します(
Vecの構築方法など)。
Unsafeを使う際の責任
unsafe を使用すると、Rustの安全性の保証を手動で維持する責任を負います。具体的には、次のことを意味します。
- メモリが有効で、正しくアラインされていることを保証する。
- 共有された可変状態を扱う際に、データ競合を防ぐ。
- ダングリングポインターや解放後使用のエラーを避ける。
- すべての関数の事前条件が満たされていることを保証する。
unsafe を誤用すると未定義動作につながります。これは最も厄介な種類のバグであり、デバッグが非常に困難になることがあります。
Unsafeの理解度チェック
Rustで次の操作のうち、unsafe ブロックが必要なものはどれでしょうか。
振り返り:Unsafe Rust
特定の高度な状況でRustのコンパイル時の安全性チェックの一部を回避できる強力な機能、Unsafe Rustについて学びました。
unsafe は借用チェッカーを完全に無効にするものではありません。メモリ安全性と不変条件に関する責任を、すべてプログラマーであるあなたに移すものです。通常はFFI、低レベルの最適化、安全な抽象化の構築などに限定し、慎重かつ十分な注意を払って使用してください。
よくある質問
「Unsafe Rustを扱う」レッスンは無料ですか?
はい。「Unsafe Rustを扱う」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Learn Rust Codingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Learn Rust Codingコースには全3レッスンが含まれています。
「Unsafe Rustを扱う」で何を学びますか?
Rustの安全性チェックを回避する`unsafe`ブロックをいつ、どのように使うかを理解し、FFIや低レベルのメモリ操作を可能にします。 ブラウザで直接実行するハンズオンコードでLearn Rust Codingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Learn Rust Codingを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのLearn Rust Codingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/3です。
「Unsafe Rustを扱う」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このLearn Rust Codingレッスンでコードを書いて実行できますか?
はい。すべてのLearn Rust Codingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- 宣言的マクロ(`macro_rules!`)
- 手続き型マクロ:Derive、Function
- Unsafe Rustを扱う