English

型システム編 - 所有権と借用

難易度: 🔴 上級
所要時間: 40分

📚 この章で学ぶこと


所有権とは

Cm言語(v0.11.0以降)では、所有権システムにより、ガベージコレクションなしでメモリ安全性を保証します。

所有権の4つのルール:

  1. 各値は、ある時点で1つの変数(所有者)だけが所有する
  2. 所有者がスコープを抜けると、値は破棄される
  3. 所有権は move キーワードで明示的に移動できる
  4. 移動後、元の変数は使用できなくなる

移動セマンティクス (v0.11.0以降)

moveキーワードを使用して、値の所有権を明示的に移動します。

moveキーワードの使用

int main() {
    // プリミティブ型も移動可能
    int x = 42;
    int y = move x;  // xの所有権がyへ移動

    println("{y}");     // OK: yが値を所有
    // println("{x}");  // エラー: xは移動済みで使用不可

    // 移動後の再代入も禁止
    // x = 50;  // エラー: 移動した変数への代入不可

    return 0;
}

構造体の移動

struct Box { int value; }

int main() {
    Box a = {value: 10};
    Box b = move a;  // 明示的な移動

    println("{}", b.value); // OK: bが所有者
    // println("{}", a.value); // エラー: aは移動済み

    return 0;
}

コピー vs 移動

プリミティブ型は明示的に移動しない限り、デフォルトでコピーされます:

int main() {
    // デフォルトのコピー動作
    int x = 10;
    int y = x;        // コピー(xとy両方が有効)
    println("{x} {y}"); // OK: 両方アクセス可能

    // 明示的な移動
    int a = 20;
    int b = move a;   // 移動
    println("{b}");    // OK
    // println("{a}"); // エラー: aは移動済み

    return 0;
}

借用(アドレス取得による借用カウント)

重要: Cm v0.11.0では、変数のアドレスを取得(&演算子)すると、その変数は「借用中」として記録されます。

借用の仕組み

int main() {
    int x = 100;
    int* px = &x;  // &演算子でxのアドレスを取得 → xは借用中になる

    // 借用中の制限:
    // int y = move x;  // コンパイルエラー: 借用中は移動不可
    // x = 200;         // コンパイルエラー: 借用中は変更不可
    // x++;             // コンパイルエラー: 借用中は変更操作不可

    println("*px = {*px}");  // ポインタ経由でのアクセスはOK
    return 0;
}

constポインタと通常のポインタ

int main() {
    // constポインタ - 参照先を変更できない
    const int value = 42;
    const int* cptr = &value;
    // *cptr = 50;  // エラー: constポインタ経由での変更は不可

    // 通常のポインタ - 参照先を変更可能
    int mutable = 10;
    int* ptr = &mutable;  // mutableは借用中になる
    *ptr = 20;  // ポインタ経由での変更はOK(ポインタ自体はconstでない)

    // ただし、mutable自体への直接操作は不可:
    // mutable = 30;  // エラー: 借用中は直接変更不可
    // mutable++;     // エラー: 借用中は変更操作不可

    return 0;
}

現在の制限事項

注意: 現在の実装では以下の制限があります:

  1. 借用カウントの解放タイミング - スコープ終了時まで借用が維持される(手動解放は未実装)
  2. 複数借用 - 同一変数への複数ポインタ取得は可能だが、すべてカウントされる
  3. 関数引数での借用 - 関数にポインタを渡しても借用は追跡されない
void use_pointer(int* p) {
    *p = 100;
}

int main() {
    int x = 50;
    use_pointer(&x);  // 関数呼び出し時の借用は追跡されない
    x = 60;  // これは現在エラーにならない(実装上の制限)
    return 0;
}

借用の制限

Cmでは、借用中の変数に以下の制限があります:

  1. 借用中の変数は移動できない - ポインタが取得された変数はmove不可
  2. 借用中の変数は変更できない - ポインタで参照中の変数への代入は禁止
  3. 借用中の変数は変更操作できない - インクリメント等も禁止

次のステップ

✅ 所有権と移動を理解した
✅ 借用(参照)の使い方がわかった
⏭️ 次は ライフタイム を学びましょう


前の章: Enum型
次の章: ライフタイム


最終更新: 2026-02-08