(struct・参照型・パッケージ)= # struct・参照型・パッケージ ## この章で学ぶこと - `struct` を使った型の作り方 - 参照やポインタが必要になる場面 - パッケージとモジュールの基本 この章から、Mojo らしい **型の考え方** がはっきり出てきます。 Python に慣れていると少し硬く感じますが、 まずは **「Mojo では `struct` が中心」** と押さえれば大丈夫です。 ## struct の基本 この章でいちばん大事なのは、**Mojo ではユーザー定義型の中心が `struct` だ** ということです。 ### `struct` とは何か `struct` は、値をまとめて扱うための型です。 フィールドとメソッドをひとつにまとめられます。 ポイントは次の通りです。 - `struct` はユーザー定義型の基本になる - フィールドはデフォルトで可変 - 初期化は `__init__` で行う - メソッドは `def` で定義する - 継承ではなく、trait や generics で振る舞いを共有する つまり、Python のクラスに少し似ていますが、 **Mojo では `struct` を軸に型を組み立てる** と捉えるとよいでしょう。 次の例は、カウンタを `struct` で表したものです。コンストラクタは **`def __init__(out self, …)`** と書き、まだ中身のないインスタンスを **初期化してから返す** 役割を担います。フィールドを変えるメソッドは **`mut self`** を付けます。 ```{literalinclude} ../../../src/part2/ch07/structs_what_counter.mojo :language: mojo ```

リスト-1: structs_what_counter.mojo

`Counter(0)` は `__init__` 呼び出しの糖衣構文です。`increment` が `value` を書き換えられるのは、受け取りが **`mut self`** だからです。読み取りだけなら `get` のように **`self` のまま** で問題ありません。 ### メソッドと初期化 `__init__` は、インスタンスを作るときの初期化処理です。 複数の形で定義することもできます。 `@implicit` を使うと、暗黙変換に関わる書き方もできます。 ただし、最初は **「自動で変換される場合がある」** くらいの理解で十分です。 `Miles` は「キロメートルをマイル換算して保持する型」です。`@implicit` を付けた `__init__` があると、**`Miles` を期待する場所に `Float64` を渡しただけで**、コンパイラがこのコンストラクタ経由で変換してくれます。 ```{literalinclude} ../../../src/part2/ch07/structs_implicit_miles.mojo :language: mojo ```

リスト-2: structs_implicit_miles.mojo

`describe_distance(10.0)` は、一見すると `Float64` を渡しているだけですが、実際には **`Miles` が必要な文脈**なので、`10.0` から `Miles` への暗黙変換が行われます。暗黙変換は読みやすさと引き換えに追いづらくもなるため、**意図して使う**ことが大切です。 ### trait の役割 Mojo では、コピーできるか、ムーブできるかといった性質を **trait** で表します。 たとえば次のような名前が出てきます。 - `Copyable` - `Movable` ここで大事なのは、**値の扱い方まで型の設計に入っている** ことです。 Python よりも、データの動きがはっきり見える言語だと理解すると整理しやすくなります。 `Copyable` を付けると、**コピー用の仕組み**(`.copy()` やコピー初期化子など)が型に付与されます。次の `Label` は **`@fieldwise_init`** でフィールド順の初期化を自動生成しつつ、**`Copyable`** で「コピー可能なラベル」として扱えます。 ```{literalinclude} ../../../src/part2/ch07/structs_copyable_label.mojo :language: mojo ```

リスト-3: structs_copyable_label.mojo

`a.copy()` は **`a` と同じ内容の別インスタンス**を作ります。Mojo ではデフォルトで値型のコピーが暗に許されるとは限らないので、**コピーを許す設計にするか**を trait で明示する、という読み方ができます(`Copyable` は **`Movable` も含意**する、とマニュアルにあります)。 詳細: [Structs](https://docs.modular.com/mojo/manual/structs/) 出典: [Mojo Manual — structs](https://docs.modular.com/mojo/manual/structs/) > **補足:** 公式では「すべて struct」という考え方が強く出てきます。まずは、Mojo の型の中心が `struct` だと押さえれば十分です。 ## 参照型 **`struct` だけでは表しにくい形がある** ことも押さえておきます。 ### 自己参照をそのまま持てない `struct` は、自分自身をそのままフィールドに持てません。 なぜなら、サイズが決められなくなるからです。 たとえば、「次のノードを持つノード」のような構造では、 そのままではうまく表せません。 **同じ型の値をそのまま入れ子にすると、型のサイズが無限に再帰して定まらない**、と理解するとよいです。実際には **`UnsafePointer`** や **`ArcPointer`** など、**ヒープ上の別オブジェクトへの参照**として「次」を表すパターンになります(詳細は [Structs — reference types](https://docs.modular.com/mojo/manual/structs/reference/))。 ### そこで参照が必要になる このような場面では、ポインタや参照の仕組みを使います。 代表的なものとして、次の名前が出てきます。 - `ref` - `Pointer` - `ArcPointer` - `UnsafePointer` ここでは、全部を細かく覚える必要はありません。 まずは、**入れ子の構造や共有を表したいときに、参照の仕組みが必要になる** と理解すれば十分です。 **`ref`** は、既存のコンテナの要素などを **エイリアス(別名)** として扱い、インプレース更新に使います。 ```{literalinclude} ../../../src/part2/ch07/reference_ref_list_double.mojo :language: mojo ```

リスト-4: reference_ref_list_double.mojo

`for ref x in xs` の `x` は各要素への可変参照なので、`x *= 2` が **リストの中身そのもの**を更新します。 次に、**ヒープに1要素分だけ確保して `UnsafePointer` で触る**最小例です。`alloc` で領域を取り、`init_pointee_copy` で値を書き込み、読み出し後に `free` で返却します。 ```{literalinclude} ../../../src/part2/ch07/reference_heap_int_cell.mojo :language: mojo ```

リスト-5: reference_heap_int_cell.mojo

これは連結リストの一片ではありませんが、**「値型のフィールドだけでは表せないとき、ヒープ+ポインタで表す」** という発想の入り口になります。 ### `unsafe` が出てくる理由 ヒープ上のメモリを直接扱う場面では、`unsafe` が出てくることがあります。 これは、**安全性の確認を自分で強く意識して扱う領域** です。 初学者の段階では、 **「普通の `struct` では足りない場面で、より低水準な仕組みが出てくる」** と押さえておけば大丈夫です。 次の `HeapInts` は、可変長に近い「要素をヒープに載せる」例です。`UnsafePointer[Int, MutExternalOrigin]` は **コンパイラの追跡外のミュータブル領域**を指すポインタ型で、`alloc`/`destroy_pointee`/`free` を **ペアで正しく**呼ぶ責任がプログラマ側にあります。 ```{literalinclude} ../../../src/part2/ch07/reference_heapints_managed.mojo :language: mojo ```

リスト-6: reference_heapints_managed.mojo

`__del__`(終了処理)で要素ごとに `destroy_pointee` し、最後にブロック全体を `free` しています。このように **資源の寿命を型のデストラクタに結びつける** と、低水準な領域でも読みやすさを保ちやすくなります。 詳細: [Structs — reference types](https://docs.modular.com/mojo/manual/structs/reference/) 出典: [Mojo Manual — structs/reference](https://docs.modular.com/mojo/manual/structs/reference/) > **補足:** この節では、実装の細部よりも「なぜ参照やポインタが必要になるのか」をつかむのが大事です。 ## Packages 最後に、コードをどうわけて管理するかを見ます。 ### 基本 - モジュールは `__init__.mojo` を含む単位で考える - パッケージは `mojopkg.toml` で設定する - `import` や `from ... import ...` で読み込む - `__init__.mojo` で公開 API を整理できる 次のレイアウトは、**`shapes` ディレクトリをパッケージ**にした例です(`__init__.mojo` があるディレクトリがパッケージ名になります)。 ```text packages_demo/ main.mojo shapes/ __init__.mojo rect.mojo util.mojo ``` `rect.mojo` に `Rect` を定義し、`util.mojo` に補助関数を置きます。 ```{literalinclude} ../../../src/part2/ch07/packages_demo/shapes/rect.mojo :language: mojo ```

リスト-7: rect.mojo

```{literalinclude} ../../../src/part2/ch07/packages_demo/shapes/util.mojo :language: mojo ```

リスト-8: util.mojo

`__init__.mojo` から **サブモジュールの名前を再エクスポート**すると、利用側はパッケージ名だけで済ませられます。 ```{literalinclude} ../../../src/part2/ch07/packages_demo/shapes/__init__.mojo :language: mojo ```

リスト-9: __init__.mojo

エントリの `main.mojo` では、`from shapes import …` の形で読み込みます。 ```{literalinclude} ../../../src/part2/ch07/packages_demo/main.mojo :language: mojo ```

リスト-10: main.mojo

`packages_demo` ディレクトリで `mojo build main.mojo` を実行するとビルドできます。パッケージ境界と公開 API の整理は、**この `__init__.mojo` の役割**として押さえてください。 ### Python と似ている点 `import` の感覚は、かなり Python に近いです。 相対 import も使えます。 - `.` は同じ階層 - `..` はひとつ上の階層 このあたりは、Python の経験があると入りやすいです。 上の `shapes/__init__.mojo` の **`from .rect import Rect`** のように、**パッケージ内の別ファイル**を `.` 起点で指します。`util` も同様に **`from .util import min_dim`** と書くと、サブモジュール名を省略した import に寄せられます。 ### Mojo らしい点 一方で、**パッケージとしてどう配るか** は Mojo 独自の意識が少し強くなります。 `mojopkg.toml` では、パッケージの情報や依存関係をまとめます。 また、`mojo package` で配布用のビルドも行います。 ここで大事なのは、**ファイルをわけるだけでなく、公開する境界も整理する** ことです。 その役目を `__init__.mojo` が担います。 たとえば **`mojo package shapes -o shapes.mojopkg`** のようにビルドすると、**ソースディレクトリの代わりに `.mojopkg` を import 先にできる**ようになります(パッケージ名は `-o` で付けたファイル名に対応。詳細は [Packages](https://docs.modular.com/mojo/manual/packages/) と [`mojo package`](https://docs.modular.com/mojo/cli/package/))。 詳細: [Packages](https://docs.modular.com/mojo/manual/packages/) 出典: [Mojo Manual — packages](https://docs.modular.com/mojo/manual/packages/) > **補足:** Python に似ていますが、Mojo では「パッケージとしてどう見せるか」を少しはっきり意識します。 ## この章を一文で言うと **Mojo では `struct` を中心に型を作り、必要に応じて参照を使い、パッケージで公開範囲を整理していきます。** ## まとめ - Mojo のユーザー定義型の中心は `struct` - メソッドや初期化も `struct` にまとめて書く - 継承ではなく、trait や generics で振る舞いを共有する - 自己参照や共有が必要な場面では、参照やポインタが出てくる - パッケージでは `__init__.mojo` と `mojopkg.toml` が重要になる Python に似て見える部分はありますが、 この章からは **「型をどう設計するか」** がより大事になってきます。