Progrust Library.

// article

Rust の所有権を深掘り - メモリ安全性の秘密.

Rust の所有権を深掘り

Rust の最大の特徴は、コンパイル時にメモリ安全性を保証する所有権システムです。本記事では、所有権(Ownership)借用(Borrowing)の関係性を詳しく探ります。

所有権とは何か

fn main() {
    let s1 = String::from("hello");
    let s2 = s1;  // 所有権がs1からs2に移動
    // println!("{}", s1);
    println!("{}", s2);    
}

借用(Borrowing)による参照

所有権を譲渡せずに値を使いたい場合、借用を使用します:

fn main() {
    let s = String::from("hello");
    let len = calculate_length(&s);  // 参照を渡す
    println!("'{}' の長さは {}", s, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

ライフタイム(Lifetime) アノテーション

複数の参照が関わるとき、コンパイラは各参照の有効期間を理解する必要があります:

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
ライフタイムのビジュアライゼーション
x: |-------- lives until here
y:    |------ lives until here
output:       |---- result must be valid

ライフタイムアノテーション 'a により、「戻り値は両方の入力と同じ期間生存する」ことをコンパイラに伝えます。

Move Semantics と Copy Semantics

値の移動

図1: スタック上の値はコピー

スタック上に完全に収まる型(i32, bool, (i32, f64) など)は Copy セマンティクスで、代入時に自動的にコピーされます。

実践的なパターン

struct User {
    name: String,
    email: String,
}

fn create_user(name: String, email: String) -> User {
    User { name, email }  // 所有権を移動してUserを返す
}

fn main() {
    let user = create_user(
        String::from("Alice"),
        String::from("[email protected]")
    );
    // userは所有権を持っている
    println!("{}", user.name);
}

所有権システムが最初は複雑に見えるかもしれませんが、一度理解すれば Rust のコード設計がいかにシンプルで安全かが見えてきます。