rust-ownership - 背景
歴史的経緯
メモリ安全性の課題
- C言語時代(1972年〜)
- ガベージコレクション(1959年〜)
- RAII(1984年〜)
- Rust所有権(2010年〜)
所有権システムの設計
Rust の設計原則:
1. 各値には所有者が1つだけ存在する
2. 所有者がスコープを抜けると値は破棄される
3. 所有権は移動するか、借用される
コンピュータサイエンス的な意味
線形型システム
所有権は線形型(Linear Types)に基づく:
// 線形型: 値は正確に1回使用される
let x = String::from("hello");
let y = x; // x から y へ所有権が移動
// x は使用不可(線形性の保証)
アフィン型システム
Rustはアフィン型(Affine Types)を採用:
// アフィン型: 値は最大1回使用される(0回も可)
let x = String::from("hello");
drop(x); // 明示的に破棄(0回使用)
借用チェッカーの仕組み
借用規則:
1. &T(不変借用): 複数同時に存在可能
2. &mut T(可変借用): 同時に1つのみ
3. &T と &mut T は同時に存在不可
これにより:
- データ競合がコンパイル時に検出
- 読み取り中の書き込みを防止
ライフタイムと領域解析
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
// 'a は x と y の共通の最小ライフタイム
// 戻り値は 'a の間有効
実践での活用
ゼロコピーパーサー
struct Parser<'a> {
input: &'a str,
position: usize,
}
impl<'a> Parser<'a> {
// 入力をコピーせずに解析
fn parse_token(&mut self) -> &'a str {
let start = self.position;
// ...
&self.input[start..self.position]
}
}
効率的なビルダーパターン
struct StringBuilder {
buffer: String,
}
impl StringBuilder {
fn append(mut self, s: &str) -> Self {
self.buffer.push_str(s);
self // 所有権を返す
}
fn build(self) -> String {
self.buffer // 所有権を移動
}
}
実世界とのギャップ
他言語との比較
| 言語 | メモリ管理 | 安全性 | 性能 |
|---|---|---|---|
| C | 手動 | 低 | 高 |
| C++ | RAII + 手動 | 中 | 高 |
| Java | GC | 高 | 中 |
| Go | GC | 高 | 中 |
| Rust | 所有権 | 高 | 高 |
学習曲線の考慮
初期の壁:
- 借用チェッカーとの戦い
- ライフタイムの理解
- "Fighting the borrow checker"
克服後:
- バグの少ないコード
- リファクタリングの安心感
- パフォーマンスの予測可能性
現実世界での制約
// 時として unsafe が必要
// 例: FFI、自己参照構造体
unsafe {
// コンパイラが検証できない操作
// 人間が安全性を保証
}