enum(列挙型)の基本
『Zig』では、enum(列挙型)を使って関連する定数値に名前を付けてまとめることができます。C言語の enum と似た基本構文に加え、メソッドの定義・タグ付き共用体(union(enum))との組み合わせなど、より表現力の高い使い方が可能です。ここでは enum の定義から応用的な使い方までを解説します。
構文
// -----------------------------------------------
// enum の基本定義
// -----------------------------------------------
const 列挙型名 = enum {
値1, // 最初の値は 0 に対応します(デフォルト)
値2,
値3,
};
// -----------------------------------------------
// 整数バッキング型を指定した enum(タグ型)
// -----------------------------------------------
const 列挙型名 = enum(整数型) {
値1 = 数値, // タグ値を明示的に指定します
値2 = 数値,
値3, // 省略すると直前の値 + 1 になります
};
// -----------------------------------------------
// enum にメソッドを定義する
// -----------------------------------------------
const 列挙型名 = enum {
値1,
値2,
// self を第1引数にするとメソッドになります
pub fn メソッド名(self: 列挙型名) 戻り値型 {
return switch (self) {
.値1 => 結果A,
.値2 => 結果B,
};
}
};
// -----------------------------------------------
// タグ付き共用体(union(enum))
// -----------------------------------------------
const 共用体名 = union(enum) {
バリアント1: 型A, // バリアント1 のとき 型A の値を持ちます
バリアント2: 型B,
バリアント3, // 値を持たないバリアント(void 相当)
};
// switch でバリアントを判定し、内部の値を取り出します
switch (共用体の値) {
.バリアント1 => |v| { /* v は 型A の値 */ },
.バリアント2 => |v| { /* v は 型B の値 */ },
.バリアント3 => { /* 値なし */ },
}
構文一覧
| 構文/メソッド | 概要 |
|---|---|
const E = enum { A, B }; | 列挙型を定義します。デフォルトのタグ型は最小整数型が自動選択されます。 |
enum(u8) { A = 1, B = 2 } | バッキング整数型を指定して enum を定義します。タグ値を明示できます。 |
.バリアント名 | enum 値をドット記法で参照します。型推論が効く文脈では型名を省略できます。 |
@intFromEnum(e) | enum 値を対応する整数に変換します。 |
@enumFromInt(n) | 整数を対応する enum 値に変換します。未定義の値は safety check でエラーになります。 |
std.meta.fields(E) | enum の全フィールド情報(名前・タグ値)を comptime で取得します。 |
pub fn method(self: E) T | enum 内にメソッドを定義します。self で自身のバリアントを受け取ります。 |
union(enum) { ... } | タグ付き共用体を定義します。各バリアントに異なる型の値を持たせられます。 |
switch (u) { .A => |v| ... } | タグ付き共用体を switch でパターンマッチングし、|v| で内部値を取り出します。 |
@tagName(e) | enum 値のバリアント名を文字列スライスとして取得します。 |
サンプルコード
psychopass_enum.zig
// psychopass_enum.zig — Zig の enum サンプルです
// PSYCHO-PASS のキャラクターを使って
// enum の定義・メソッド・タグ付き共用体との組み合わせを確認します
const std = @import("std");
// -----------------------------------------------
// enum 定義 — 執行官の犯罪係数ランク
// -----------------------------------------------
// 犯罪係数のランクを enum で表現します
// バッキング型として u8 を指定し、タグ値を明示します
const CrimeCoefficient = enum(u8) {
Clear = 0, // 平常域(100未満)
Caution = 1, // 注意域(100〜149)
Danger = 2, // 危険域(150〜299)
Criminal = 3, // 執行対象(300以上)
// ランクに対応する日本語ラベルを返すメソッドです
pub fn label(self: CrimeCoefficient) []const u8 {
return switch (self) {
.Clear => "平常域",
.Caution => "注意域",
.Danger => "危険域",
.Criminal => "執行対象",
};
}
// 犯罪係数の数値からランクを判定するメソッドです
pub fn fromValue(value: u32) CrimeCoefficient {
if (value < 100) return .Clear;
if (value < 150) return .Caution;
if (value < 300) return .Danger;
return .Criminal;
}
// 執行対象かどうかを返すメソッドです
pub fn isExecutable(self: CrimeCoefficient) bool {
return self == .Criminal;
}
};
// -----------------------------------------------
// enum 定義 — 公安局の役職
// -----------------------------------------------
const Role = enum {
Enforcer, // 執行官
Inspector, // 監視官
Analyst, // 分析官
};
// -----------------------------------------------
// タグ付き共用体 — 装備品の種類
// -----------------------------------------------
// 各バリアントが異なるデータ型を持つタグ付き共用体です
const Weapon = union(enum) {
dominator: u32, // ドミネーター(最大出力値を保持します)
stun_baton: void, // スタンバトン(追加データなし)
none: void, // 非武装
};
// キャラクター情報を表す構造体を定義します
const Officer = struct {
name: []const u8,
role: Role,
coefficient: u32, // 犯罪係数
weapon: Weapon,
};
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
// -----------------------------------------------
// enum の基本操作
// -----------------------------------------------
// PSYCHO-PASS のキャラクターを定義します
const officers = [_]Officer{
.{ .name = "常守朱", .role = .Inspector, .coefficient = 28, .weapon = .{ .dominator = 480 } },
.{ .name = "宜野座伸元", .role = .Enforcer, .coefficient = 157, .weapon = .{ .dominator = 640 } },
.{ .name = "狡噛慎也", .role = .Enforcer, .coefficient = 284, .weapon = .{ .dominator = 620 } },
.{ .name = "征陸智己", .role = .Enforcer, .coefficient = 201, .weapon = .stun_baton },
.{ .name = "唐之杜志恩", .role = .Analyst, .coefficient = 45, .weapon = .none },
};
try stdout.print("=== 公安局 刑事課一係 ステータス ===\n\n", .{});
for (officers) |officer| {
// 犯罪係数の数値から enum ランクを取得します
const rank = CrimeCoefficient.fromValue(officer.coefficient);
// メソッドを呼び出してラベルを取得します
const rank_label = rank.label();
// 役職を switch で日本語に変換します
// enum の全値を列挙しているため else は不要です
const role_label = switch (officer.role) {
.Inspector => "監視官",
.Enforcer => "執行官",
.Analyst => "分析官",
};
try stdout.print("【{s}】{s} / 係数: {d} ({s})\n", .{
officer.name,
role_label,
officer.coefficient,
rank_label,
});
// isExecutable() で執行対象かどうかを判定します
if (rank.isExecutable()) {
try stdout.print(" ⚠ 執行対象に指定されています\n", .{});
}
}
// -----------------------------------------------
// @intFromEnum / @tagName の使用例
// -----------------------------------------------
try stdout.print("\n=== 犯罪係数ランク一覧 ===\n\n", .{});
// std.meta.fields() で enum の全フィールドを comptime で取得します
inline for (std.meta.fields(CrimeCoefficient)) |field| {
// @enumFromInt() で整数から enum 値を取得します
const rank: CrimeCoefficient = @enumFromInt(field.value);
// @intFromEnum() で enum 値を整数に変換します
const tag_int: u8 = @intFromEnum(rank);
// @tagName() でバリアント名の文字列を取得します
const tag_str = @tagName(rank);
try stdout.print(" タグ値 {d}: {s} ({s})\n", .{ tag_int, tag_str, rank.label() });
}
// -----------------------------------------------
// タグ付き共用体(union(enum))の操作
// -----------------------------------------------
try stdout.print("\n=== 装備品情報 ===\n\n", .{});
for (officers) |officer| {
// switch で union のバリアントを判定します
// |v| でバリアントが持つ内部値を取り出します
const weapon_desc: []const u8 = switch (officer.weapon) {
.dominator => |max_output| blk: {
// ブロック式で文字列を選択します
// 最大出力が 600 以上かどうかでモードを判定します
_ = max_output;
break :blk if (officer.weapon.dominator >= 600)
"ドミネーター(レサルエリミネーター対応)"
else
"ドミネーター(パラライザーモード)";
},
.stun_baton => "スタンバトン",
.none => "非武装",
};
try stdout.print("{s}: {s}\n", .{ officer.name, weapon_desc });
}
}
zig run psychopass_enum.zig === 公安局 刑事課一係 ステータス === 【常守朱】監視官 / 係数: 28 (平常域) 【宜野座伸元】執行官 / 係数: 157 (危険域) 【狡噛慎也】執行官 / 係数: 284 (危険域) 【征陸智己】執行官 / 係数: 201 (危険域) 【唐之杜志恩】分析官 / 係数: 45 (平常域) === 犯罪係数ランク一覧 === タグ値 0: Clear (平常域) タグ値 1: Caution (注意域) タグ値 2: Danger (危険域) タグ値 3: Criminal (執行対象) === 装備品情報 === 常守朱: ドミネーター(パラライザーモード) 宜野座伸元: ドミネーター(レサルエリミネーター対応) 狡噛慎也: ドミネーター(レサルエリミネーター対応) 征陸智己: スタンバトン 唐之杜志恩: 非武装
概要
『Zig』の enum は、関連する定数値に名前を付けてまとめる型です。デフォルトでは最小整数型がタグ型として自動選択されますが、enum(u8) のようにバッキング型を明示することでタグ値の範囲と表現を制御できます。@intFromEnum() で enum 値を整数に、@enumFromInt() で整数を enum 値に相互変換できます。また @tagName() でバリアント名を文字列として取得でき、デバッグ出力やログに活用できます。
enum にはメソッドを定義できます。pub fn メソッド名(self: EnumType) の形で書くと、値.メソッド名() のように呼び出せます。switch (self) と組み合わせることで、バリアントごとに異なる処理やデータを返すロジックを enum 自身に持たせられます。std.meta.fields() を使うと comptime に全フィールドを列挙でき、汎用的な処理に役立ちます。
タグ付き共用体(union(enum))は、enum のバリアントごとに異なる型の値を保持できる型です。各バリアントがどのデータを持つかを型レベルで表現できるため、Rust の enum(代数的データ型)に近い表現が可能です。switch でバリアントを判定し、|v| キャプチャ構文で内部値を安全に取り出します。switch を使ったパターンマッチングの詳細は switch を、struct との組み合わせは struct の基本 も合わせて確認してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。