Rustは「安全性(safety)」を徹底的に追求した言語です。
クラッシュ・メモリリーク・データ競合といった低レイヤーでよくあるバグを、Rustは**コンパイル時に“書けないようにする”**ことで防ぎます。
本記事では、「なぜRustが安全なのか?」を、実際のコード例を通してわかりやすく解説していきます。
🔹 目次
- 二重解放 vs 所有権
- ダングリングポインタ vs ライフタイム
- Nullポインタ vs Option型
- データ競合 vs 借用チェック
- スレッド安全 vs 所有権とSend/Sync
- unsafeとその使いどころ
- まとめ
1. 🧨 二重解放を防ぐ:所有権の仕組み
C++の場合(危険なコード)
int* ptr = new int(10);
delete ptr;
delete ptr; // 二重解放 → 未定義動作!
Rustの場合(安全)
fn main() {
let a = Box::new(10);
let b = a;
// println!("{}", a); // ❌ 所有権がbに移動したのでaは使えない(コンパイルエラー)
}
Rustでは所有権が1つしか存在できず、所有権を移動したら元の変数は使えなくなるため、二重解放が起こりません。
2. 🧟 ダングリングポインタを防ぐ:ライフタイム
C/C++の例(使ってはいけない戻り値)
int* getPtr() {
int x = 10;
return &x; // スタックの寿命を超えてポインタが返る
}
Rustではコンパイルエラーになる!
fn get_ref() -> &i32 {
let x = 10;
&x // ❌ コンパイルエラー:xの寿命が短すぎる
}
Rustはライフタイム(寿命)を明示・推論することで、有効期間外の参照を禁止しています。
3. ❓ Nullを防ぐ:Option型による明示的な扱い
C++の場合(nullチェック漏れ)
std::string* ptr = nullptr;
std::cout << ptr->length(); // Segmentation fault!
Rustでは Option<T>
で安全に扱う
fn get_name(flag: bool) -> Option<String> {
if flag {
Some("Rust".into())
} else {
None
}
}
fn main() {
match get_name(false) {
Some(name) => println!("名前: {}", name),
None => println!("名前なし"),
}
}
Rustでは「null」は存在しません。「値がある/ない」を型で表現するため、null参照によるクラッシュを根本的に防げます。
4. 🔁 データ競合を防ぐ:借用チェッカー
C++なら起こる競合の例
int x = 42;
int* a = &x;
int* b = &x;
*a = 10;
*b = 20; // 同時に書き換え → レースコンディションの原因に
Rustでは可変参照は1つだけ
fn main() {
let mut x = 42;
let a = &mut x;
let b = &mut x; // ❌ エラー:同時に2つの可変参照は不可
}
Rustの借用ルールにより、**「読み取り専用は複数OK」「書き込みは1つだけ」**という制約が守られます。
5. 🧵 スレッド間の安全性:Send / Sync による制御
Rustはスレッドセーフがデフォルトではない(明示)
use std::thread;
fn main() {
let msg = String::from("hello");
let handle = thread::spawn(move || {
println!("{}", msg); // 所有権ごとmoveする必要あり
});
handle.join().unwrap();
}
- Rustではスレッドに渡すデータは
Send
+Sync
のトレイト制約あり - 誤って共有メモリを参照するとコンパイルエラーになるため、レースコンディションを防げる
6. 🧯 unsafeって何?なぜ存在する?
Rustの安全性は強力ですが、どうしても以下のようなケースでは「安全チェックを一時的に無効化」したくなることがあります:
- FFI(C言語との連携)
- メモリアロケータの実装
- パフォーマンスの限界に挑むとき
unsafe fn dangerous() {
println!("unsafeコードです");
}
fn main() {
unsafe {
dangerous(); // unsafeブロック内でのみ実行可能
}
}
→ unsafeは「危険だけど自己責任でやります」という宣言。Rustはその範囲を明示させることで全体の安全性を維持します。
✅ まとめ:Rustは「バグを書けなくする言語」
言語 | 安全性へのアプローチ |
---|---|
C/C++ | バグを避ける or テストで見つける |
Java/Python | ランタイムで例外にする |
Rust | コンパイル時にバグを“書けなくする” |
Rustの安全性は「面倒そう」に見えても、一度習得すれば大きなバグが激減します。