メインコンテンツまでスキップ

構造体

概要

Wave言語の構造体はカスタムデータ型を宣言するためのコア構文要素です。 構造体を使用すると異なるタイプの値を1つの論理単位としてまとめ表現でき、これを通じて複雑なデータ構造を明確かつ安全にモデリングできます。

Waveの構造体は値タイプ(value type)として動作します。 すべてのフィールドは必ず明示的なタイプを持たなければならず、構造体インスタンスを生成する際にすべてのフィールドが初期化されなければなりません。 これらのルールを通じて、構造体の状態は常に完全で予測可能な形を維持します。

構造体宣言の文法

構造体はstructキーワードを用いて宣言します。 構造体の名前はパスカル表記法(PascalCase)を使用し、構造体本文には1つ以上のフィールドを定義できます。

フィールドは名前: タイプ;形式で宣言され、各フィールド宣言の後には必ずセミコロンが必要です。

struct Box {
size: i32;
weight: f32;
}

構造体宣言時にフィールドが記述された順序はメモリ配置順序と同様に使用されます。 構造体の内部にはフィールド宣言のみが許可され、関数やメソッドを含むことはできません。 動作ロジックは構造体外部で別途定義されます。

構造体生成の文法

構造体は構造体名を用いるリテラル形式で生成します。 構造体リテラルはStructName { フィールド名: 値; ... }形式で記述します。

var b: Box = Box {
size: 42;
weight: 10.5;
};

構造体を生成する際は、定義されたすべてのフィールドを必ず初期化し、1つでも漏れがあるとコンパイルエラーが発生します。

初期化時にフィールドの記述順序は構造体宣言順序と一致する必要はありませんが、各フィールドに渡される値のタイプは構造体で定義されたタイプと正確に一致しなければなりません。 Waveでは構造体フィールドの初期化過程で暗黙の型変換を許可しません。


構造体フィールドアクセスの文法

構造体のフィールドにはドット表記法を用いてアクセスします。 フィールドアクセスには読み書き共に同じ文法を使用します。

println("Size: {}", b.size);
println("Weight: {}", b.weight);

存在しないフィールド名を使用しようとするとコンパイル段階でエラーが発生します。 構造体は値タイプであるため、構造体全体を代入したり関数引数として渡す場合、構造体に含まれるすべてのフィールドが一緒にコピーされます。


構造体メソッド定義の文法

Wave言語は構造体内部に直接メソッドを定義しません。 代わりにprotoキーワードを用いて構造体に関連付けられたメソッド集合を宣言します。

protoブロックは特定の構造体に所属する関数の領域であり、このブロック内で定義された関数はその構造体のメソッドのように使用されます。

メソッドは最初のパラメーターとしてselfを使用して構造体インスタンスを受け取ります。 selfは構造体全体の値を意味し、値コピー方式で渡されます。

proto Box {
fun print(self) {
println("サイズ={}, 重さ={}", self.size, self.weight);
}

fun added_size(self, x: i32) -> i32 {
return self.size + x;
}
}

protoブロックは構造体宣言と同じファイルに位置する必要はなく、 複数のprotoブロックを使って同一の構造体に対するメソッドを分散定義することができます。

メソッド呼び出しはドット表記を使用し、一般的な関数呼び出しと同じ方法で動作します。

b.print();
var n: i32 = b.added_size(5);

関数引数としての構造体の使用

構造体は関数の引数として渡されるとき、値コピー方式で処理されます。 関数内部で構造体のフィールドを修正しても、呼び出した側の構造体インスタンスには影響を与えません。

fun calc(box: Box) -> i32 {
return box.size * 2;
}

構造体を関数の戻り値として使用する場合でも同様に値コピーが発生します。 このような動作は、構造体の不変性と予測可能なデータフローを保証します。


ネスト構造体(Nested Struct)

Waveでは構造体のフィールドタイプとして他の構造体を使用できます。 構造体は完全なタイプであるため、構造体内にさらに別の構造体を含む形で自由にネストできます。

struct Position {
x: i32;
y: i32;
}

struct Player {
name: str;
pos: Position;
}

ネストされた構造体のフィールドには、連続したドット表記を使用してアクセスします。

var p: Player = Player {
name: "Alice";
pos: Position { x: 10; y: 20; };
};

println("プレイヤー X: {}", p.pos.x);
println("プレイヤー Y: {}", p.pos.y);

構造体リテラル内に別の構造体リテラルをネストして書くことができ、 この場合でもすべてのフィールド初期化規則は同じように適用されます。


構造体配列

構造体は配列の要素タイプとして使用できます。 Waveの配列文法はarray<タイプ, 長さ>形式を使い、構造体タイプもそのまま指定できます。

var players: array<Player, 3> = [
Player { name: "A"; pos: Position { x: 1; y: 2; }; },
Player { name: "B"; pos: Position { x: 3; y: 4; }; },
Player { name: "C"; pos: Position { x: 5; y: 6; }; }
];

構造体配列の要素にアクセスする際には、まず配列インデックスを使用し、その後、ドット表記法を使って構造体内のフィールドにアクセスします。

println("2番目のプレイヤー X: {}", players[1].pos.x);

構造体の基本演算可能かどうか

Waveの構造体はユーザー定義タイプであるため、 算術演算や比較演算に自動では参加しません。

構造体の同等性比較、ソート、ハッシュ化、数値演算などが必要な場合、必ずprotoブロックを通してその動作を明示的に定義する必要があります。 Waveは構造体に対して暗黙のオペレーターを提供せず、すべての動作は明示的に定義されることが原則です。