(値・所有権・ライフサイクル)= # 値・所有権・ライフサイクル ## この章で学ぶこと - 値と所有権の基本 - ライフタイムとライフサイクルの違い - Initialization や Death の考え方 この章は、Mojo の中でも最初に少し難しく感じやすい部分です。 ただし、最初から細部まで理解しなくても大丈夫です。 まずは次の3つを押さえると読みやすくなります。 - 値には持ち主がある - 参照は好きなだけ長く使えるわけではない - 値は作られてから捨てられるまでの流れを持つ この章は、[Mojo Manual — Values / Lifecycle](https://docs.modular.com/mojo/manual/values/) に対応する内容です。 ## Values 最初に大事なのは、**Mojo では値がどこに置かれ、誰が片づけるかをはっきり意識する** ことです。 ### 基本 - 値はスタックまたはヒープに置かれる - 所有権が、いつ誰が解放するかを決める - 参照は借りているだけなので、元の値より長くは使えない 次の例では、小さな **`Int`** のローカルと、要素を持つ **`List[Int]`** を並べています。`Int` は機械語に近いイメージではレジスタやスタック枠に載りやすく、**`List`** のような可変長コレクションはヒープ側に本体を持つ、とざっくり理解してよいです(正確な配置は実装依存ですが、**「所有と解放の話」に入る入口**として使えます)。 ```{literalinclude} ../../../src/part2/ch08/values_int_and_list.mojo :language: mojo ```
リスト-1: values_int_and_list.mojo
Mojo には **GC(ガベージコレクタ)がありません**。`List` のようにヒープを使う型は、**型の規則と所有権**のもとで、不要になったときに解放される、という考え方で読みます。 ### まずどう理解すればよいか 小さくて扱いやすい値は、スタックに置かれることがあります。 一方で、大きいものや可変長のものは、ヒープに置かれることがあります。 ここで大事なのは、**値の置き場所そのものより、誰がその値の後始末をするか** です。 Mojo では、その役目を **所有権** が決めます。 また、Mojo には GC がありません。 その代わり、値がいつ破棄されるかを、よりはっきり扱います。 後の節で見る **ムーブ**(`^`)や **デストラクタ**と結びつきます。ここでは **「値ごとに持ち主のイメージを持つ」** ことが第一歩です。 詳細: [Values](https://docs.modular.com/mojo/manual/values/) 出典: [Mojo Manual — values](https://docs.modular.com/mojo/manual/values/) > **補足:** ここは ownership と lifetimes の入口です。最初は「値には持ち主がいる」と理解すれば十分です。 ## Value semantics 次に大事なのは、**Mojo は値セマンティクスを基本にしている** ことです。 ### 値セマンティクスとは 簡単に言うと、**値を渡すときは、ひとまず独立したものとして考える** ということです。 ポイントは次の通りです。 - 代入や引数渡しでは、論理的に独立した値として考える - 実際にはムーブで効率よく処理されることがある - 関数引数は `read` 参照が基本 - 書き換えたいときは `mut` などを明示する `Counter` は `n` を内側に持つ小さな値型です。`show(c)` は **`c` を読むだけ**の用途なので、引数は暗黙の共有読み取り(**`read`** に相当する扱い)として渡せます。一方 **`c.bump()`** は **`mut self`** で自分自身を更新します。 ```{literalinclude} ../../../src/part2/ch08/value_semantics_counter.mojo :language: mojo ```リスト-2: value_semantics_counter.mojo
`show` を二回呼んでも、**「共有オブジェクトを二か所からいじる」** という Python 的な絵ではなく、**その時点の `Counter` の値を読む**という解釈に近いです(実際のコピー/ムーブは型と呼び出し方に依存します)。 ### Python との違い Python では、同じオブジェクトを複数の名前で共有している感覚が強いです。 一方で Mojo では、**値をどう渡すか** をもっとはっきり区別します。 そのため、 - 読むだけなのか - 書き換えるのか - 持ち主ごと渡すのか を意識して読むことが大切です。 **「名前が同じオブジェクトを指す」** より **「その操作は共有読み取りか、単一の所有者の変更か」** を追うと、Mojo の値セマンティクスに慣れやすくなります。 詳細: [Value semantics](https://docs.modular.com/mojo/manual/values/value-semantics/) 出典: [Mojo Manual — value-semantics](https://docs.modular.com/mojo/manual/values/value-semantics/) > **補足:** Python の「参照を共有する感じ」とは少し違う、と感じられれば十分です。 ## Ownership この節の中心は、**値を誰が持っていて、誰が触れてよいかを区別する** ことです。 ### よく出てくる見方 - `read` は共有して読む - `mut` は書き換えのための扱い - `var` は所有していて移動できる - `ref` は借用を表す 次のコードは、**`read` でリストを借りて合計する**、**`ref` で要素を直接書き換える**、**`var` で所有するリストを `^` で渡して移動する**、を一度に示したものです。 ```{literalinclude} ../../../src/part2/ch08/ownership_read_ref_and_move.mojo :language: mojo ```リスト-3: ownership_read_ref_and_move.mojo
`sum_borrow(xs)` は **`xs` の所有を奪わず**、読み取りに必要な期間だけ借用します。`for ref x in xs` は各要素への **可変借用**で、インプレース更新です。最後の **`take_owned(data^)`** は、`data` の **所有権を callee に渡し切る**ので、その後 `data` は使えません(コンパイラが防ぎます)。 ### `^` の意味 `^` は、**所有権を移す** ことを表します。 つまり、値を相手に渡し切る記号です。 そのため、移動したあとの元の変数はそのまま使えません。 これは少し厳しく見えますが、 **どの値を誰が使ってよいかを曖昧にしないための仕組み** です。 ### なぜ厳しくするのか Mojo は、複数の場所から同時に危険な書き換えが起こるのを防ごうとします。 たとえば、複数の `mut` を同時に許さないのは、そのためです。 こうした制約によって、データ競合をコンパイル時に防ぎやすくなります。 **`read` と `ref` と `^`** を混ぜると、「今この瞬間、誰が唯一の所有者で、誰が一時的に借りているか」がはっきりします。意図しない共有可変を減らす、というのが厳しさの理由です。 詳細: [Ownership](https://docs.modular.com/mojo/manual/values/ownership/) 出典: [Mojo Manual — ownership](https://docs.modular.com/mojo/manual/values/ownership/) > **補足:** 記号や用語を全部一度に覚える必要はありません。まずは「読む」「書く」「所有する」「借りる」を分けて考えれば大丈夫です。 ## Lifetimes ここでは、**参照がどこまで有効か** を考えます。 ### ライフタイムとは ライフタイムは、**その参照を安全に使える期間** のことです。 参照は元の値を借りているだけなので、元の値が消えたあとまで使ってはいけません。 `read` 引数は、**呼び出し元の `buf` が生きているあいだだけ**借用が有効です。関数を抜けたあとに参照を返すような形は、コンパイラが(多くの場合)許可しません。 ```{literalinclude} ../../../src/part2/ch08/lifetimes_read_param.mojo :language: mojo ```リスト-5: lifetimes_read_param.mojo
### ここで大事なこと - 参照には有効期間がある - 関数が参照を返すなら、その参照元がはっきりしている必要がある - ダングリング参照は拒否される ダングリング参照とは、もう存在しない値を指してしまう参照です。 これは危険なので、Mojo では型の仕組みで防ごうとします。 **ダングリングを起こすコードは、このドキュメントでは意図的に載せていません**(コンパイルエラーになるため)。上の例のように、**借用は所有者のスコープに収まる**と読むと、ライフタイムの感覚をつかみやすいです。 詳細: [Lifetimes](https://docs.modular.com/mojo/manual/values/lifetimes/) 出典: [Mojo Manual — lifetimes](https://docs.modular.com/mojo/manual/values/lifetimes/) > **補足:** ここは難所です。まずは「参照は元の値より長生きできない」と押さえておくとよいでしょう。 ## Lifecycle 次は、値の一生をもう少し大きく見ます。 ### ライフサイクルとは ライフサイクルは、**値が作られてから壊されるまでの全体の流れ** です。 一方でライフタイムは、主に **参照の有効期間** を見ています。 つまり、 - ライフサイクルは値全体の一生 - ライフタイムは参照の有効期間 という違いがあります。 `Resource` は **`__init__` で生成ログ**、**`__del__` で終了ログ**を出します。`main` の終わりで `r` のスコープを抜けるときに **`__del__` が呼ばれる**、という順序が、実行時の出力として追えます。 ```{literalinclude} ../../../src/part2/ch08/lifecycle_resource_traces.mojo :language: mojo ```リスト-6: lifecycle_resource_traces.mojo
### 何がうれしいのか この考え方を持つと、 - いつ値が作られるか - いつ初期化されるか - いつ破棄されるか を整理して考えられます。 Mojo では、不要になった値を早めに解放する方向で考えます。 実行すると `init` → `use` → `del` の順にメッセージが出ます。 詳細: [Lifecycle](https://docs.modular.com/mojo/manual/lifecycle/) 出典: [Mojo Manual — lifecycle](https://docs.modular.com/mojo/manual/lifecycle/) > **補足:** ここでは用語をきっちり分けることが大事です。ライフタイムとライフサイクルは同じではありません。 ## Life この節は、**値がどう生まれるか** に注目します。 ### 基本 - `__init__` で値を初期化する - コピーでは `__copyinit__` が関わる - ムーブでは `__moveinit__` が関わる - 代入時の動きも型によって決まる 初学者の段階では、細かな特殊メソッドを全部覚える必要はありません。 まずは、**作る方法・コピーする方法・移す方法がわかれている** とわかれば十分です。 **`Copyable`** を付けた型は、**コピー**の道筋がはっきりします。`@fieldwise_init` で **コンストラクタを自動生成**しつつ、`a.copy()` で **明示的に複製**できます。 ```{literalinclude} ../../../src/part2/ch08/life_copyable_label.mojo :language: mojo ```リスト-7: life_copyable_label.mojo
`__copyinit__` を手で書かなくても、`Copyable` とフィールド構成に応じて **コピー初期化子**が生成される、とマニュアルに沿って読めます。**ムーブ**は別経路(`^` やコンテキスト)で、`Life` の話と対にして整理します。 詳細: [Life](https://docs.modular.com/mojo/manual/lifecycle/life/) 出典: [Mojo Manual — life](https://docs.modular.com/mojo/manual/lifecycle/life/) > **補足:** Life と Death は対になる話です。作り方と壊し方をセットで見ると整理しやすいです。 ## Initialization Initialization は、**値をちゃんと使える状態にするまでの手順** です。 ### ポイント - フィールドごとに初期化する場合がある - 最後に全体として整った状態にする必要がある - `__init__` の分け方で初期化の流れを変えられる ここで大事なのは、**とりあえず値を入れればよいわけではない** ことです。 型として正しい状態になって、はじめて安全に使えます。 `IntRange` は **`center` と `width` から `lo` / `hi` を計算して**フィールドを埋めます。呼び出し側は **意味のある引数**だけ渡せばよく、**不変条件**(`lo <= hi` など)をコンストラクタ内に閉じ込めやすいです。 ```{literalinclude} ../../../src/part2/ch08/init_box_bounds.mojo :language: mojo ```リスト-8: init_box_bounds.mojo
詳細: [Initialization](https://docs.modular.com/mojo/manual/lifecycle/initialization/) 出典: [Mojo Manual — initialization](https://docs.modular.com/mojo/manual/lifecycle/initialization/) > **補足:** 具体例は公式の `struct` の例と併せて確認すると理解が深まります。 ## Death 最後に、**値がどう終わるか** を見ます。 ### 基本 - `__del__` はデストラクタ - リソース解放に関わる - 明示的に破棄させる型もある - ムーブ後や二重解放の問題も意識する ### ここで見ておきたいこと ファイルやメモリなど、後始末が必要なものがあります。 その後始末を担うのが `__del__` です。 また、同じものを二重に解放すると危険です。 Mojo では、そのような問題を型や規約で防ごうとします。 `HeapInts` は **ヒープに `Int` を並べて保持**し、`__del__` で **各要素の破棄と `free`** を行います。`lifecycle_resource_traces.mojo` のログ付きデストラクタと対になる、**実リソースを返す**例です。 ```{literalinclude} ../../../src/part2/ch08/death_heap_buffer.mojo :language: mojo ```リスト-9: death_heap_buffer.mojo
`main` を抜けて `h` のスコープが終わると **`__del__` が走り**、ヒープが返却されます。**ムーブ**で所有が移ったあとに同じバッファを二重に `free` しないよう、型システムとムーブの規則が関わります(詳細はマニュアルの Ownership / Death を参照)。 詳細: [Death](https://docs.modular.com/mojo/manual/lifecycle/death/) 出典: [Mojo Manual — death](https://docs.modular.com/mojo/manual/lifecycle/death/) > **補足:** ここは RAII の考え方につながります。最初は「必要な後始末を自動で安全に行う仕組みがある」と押さえれば十分です。 ## この章を一文で言うと **Mojo では、値の持ち主・参照の有効期間・生成から破棄までの流れを、型の仕組みで明確に扱います。** ## まとめ - 値には持ち主があり、所有権が後始末を決める - 値セマンティクスでは、値の受け渡しを独立したものとして考える - `read`、`mut`、`var`、`ref` で役割がわかれる - ライフタイムは参照の有効期間を表す - ライフサイクルは値全体の一生を表す - `__init__` と `__del__` は、作るときと終わるときの中心になる この章は一度で完全に理解しなくても大丈夫です。 まずは、**「誰が持つか」「いつまで使えるか」「いつ片づけるか」** の3つで整理すると読みやすくなります。