Ziglings 笔记 37: 万物皆可结构体 (Structs)

数据的容器

在 Ziglings 的第 37 个练习中,我们终于遇到了 Struct (结构体)。 在此之前,如果我们想描述一个游戏角色,可能需要定义 role_wiz, gold_wiz, hp_wiz 等一堆散乱的变量。而结构体允许我们将这些相关的数据打包在一起。

挑战:RPG 角色卡

任务是为一个 RPG 角色系统添加生命值(Health)属性:

  1. Character 类型定义中添加 health 字段。
  2. 在初始化角色 glorp_the_wise 时设置具体的生命值。

解决方案

这是完整的代码实现:

const std = @import("std");

const Role = enum {
    wizard,
    thief,
    bard,
    warrior,
};

// 1. 定义结构体
// struct 关键字用于定义一组字段的集合
const Character = struct {
    role: Role,
    gold: u32,
    experience: u32,
    health: u8, // 新增:生命值,使用 u8 (0-255) 就够了
};

pub fn main() void {
    // 2. 初始化结构体实例
    // 注意:Zig 要求必须初始化所有字段(除非字段有默认值)
    var glorp_the_wise = Character{
        .role = Role.wizard,
        .gold = 20,
        .experience = 10,
        
        // 关键语法:字段名前面要加点 (.)
        .health = 100,
    };

    glorp_the_wise.gold += 5;
    glorp_the_wise.health -= 10;

    std.debug.print("Your wizard has {} health and {} gold.\n", .{
        glorp_the_wise.health,
        glorp_the_wise.gold,
    });
}

核心知识点总结

1. 语法解剖

Zig 的结构体初始化语法非常独特:

MyStruct{
    .field1 = value1,
    .field2 = value2,
}

那个前导点号 (.) 是必须的。它不仅让代码在视觉上更清晰(一眼看出这是字段赋值),而且有助于编译器进行类型推导。

2. 内存布局

虽然这个练习没有涉及,但值得一提的是,Zig 的 struct 默认不保证内存布局顺序(为了优化对齐,编译器可能会重排字段)。如果你需要和 C 语言交互,需要使用 extern struct

3. 类型安全

结构体让数据传递变得安全。你不能把一个 Character 传给一个需要 Item 的函数,即使它们碰巧有相同的字段。


后续预告:结构体只能存数据吗?在 Zig 中,结构体其实可以像类(Class)一样拥有方法(Methods)。下一篇我们将学习如何把函数放进结构体里。