解答10: 構造体と列挙型の完全理解

概要

この解答では、Zigの複合型である構造体、列挙型、共用体について学びます。

Part 1: 構造体

const std = @import("std");

const Point = struct {
    x: f32,
    y: f32,

    pub fn init(x: f32, y: f32) Point {
        return .{ .x = x, .y = y };
    }

    pub fn distance(self: Point, other: Point) f32 {
        const dx = self.x - other.x;
        const dy = self.y - other.y;
        return @sqrt(dx * dx + dy * dy);
    }

    pub fn translate(self: *Point, dx: f32, dy: f32) void {
        self.x += dx;
        self.y += dy;
    }
};

const Config = struct {
    timeout_ms: u32 = 5000,
    max_retries: u8 = 3,
    verbose: bool = false,
};

Part 2: 列挙型

const std = @import("std");

const Direction = enum {
    north,
    south,
    east,
    west,

    pub fn opposite(self: Direction) Direction {
        return switch (self) {
            .north => .south,
            .south => .north,
            .east => .west,
            .west => .east,
        };
    }
};

const HttpStatus = enum(u16) {
    ok = 200,
    created = 201,
    bad_request = 400,
    not_found = 404,

    pub fn isSuccess(self: HttpStatus) bool {
        return @intFromEnum(self) >= 200 and @intFromEnum(self) < 300;
    }
};

Part 3: 共用体

const std = @import("std");

const Value = union(enum) {
    int: i64,
    float: f64,
    string: []const u8,
    boolean: bool,
    null_val,

    pub fn isNumeric(self: Value) bool {
        return switch (self) {
            .int, .float => true,
            else => false,
        };
    }
};

Part 4: メモリレイアウト

const std = @import("std");

const Normal = struct {
    a: u8,  // + padding
    b: u32,
    c: u8,  // + padding
};

const Packed = packed struct {
    a: u8,
    b: u32,
    c: u8,
};

pub fn layoutDemo() void {
    std.debug.print("Normal size: {}, Packed size: {}\n", .{
        @sizeOf(Normal),
        @sizeOf(Packed),
    });
}

ポイント解説

特性 struct enum union
データ格納 全フィールド同時 単一の値 いずれか一つ
用途 データのグループ化 状態の列挙 バリアント型

よくある間違い

// 誤り: 変更メソッドでコピーを受け取る
pub fn increment(self: Counter) void {
    self.value += 1; // コピーを変更
}

// 正しい: ポインタで受け取る
pub fn increment(self: *Counter) void {
    self.value += 1;
}

自己確認チェックリスト

  • [ ] selfとself: *の使い分けを理解しているか
  • [ ] タグ付き共用体の用途を説明できるか
  • [ ] struct, packed struct, extern structの違いを把握しているか