Ziglings 笔记 47: 结构体的方法 (Methods)
它是方法,也是函数
在 Ziglings 的第 47 个练习中,我们迎来了一个重大的语法特性:方法 (Methods)。
如果你习惯了 Java 或 C++ 的“类 (Class)”,你可能会觉得 Zig 的 struct 只是数据的容器。但通过这个练习,你会发现 Zig 的结构体其实也能包含行为。
挑战:外星人入侵
我们需要模拟一场战斗:
Alien结构体有一个“孵化”函数hatch。HeatRay(热射线)结构体有一个“攻击”函数zap。- 我们需要用热射线遍历并消灭所有的外星人。
解决方案
这是实现攻击逻辑的代码:
const std = @import("std");
const Alien = struct {
health: u8,
// 静态函数风格:
// 第一个参数不是 Alien 或 *Alien,所以只能用 Alien.hatch(...) 调用
pub fn hatch(strength: u8) Alien {
return Alien{
.health = strength * 5,
};
}
};
const HeatRay = struct {
damage: u8,
// 方法风格:
// 第一个参数是 self: HeatRay。
// 这意味着我们可以用 dot syntax: instance.zap(...)
pub fn zap(self: HeatRay, alien: *Alien) void {
alien.health -= if (self.damage >= alien.health) alien.health else self.damage;
}
};
pub fn main() void {
var aliens = [_]Alien{
Alien.hatch(2),
Alien.hatch(1),
// ...
};
var aliens_alive = aliens.len;
const heat_ray = HeatRay{ .damage = 7 };
while (aliens_alive > 0) {
aliens_alive = 0;
// 遍历所有外星人(获取指针)
for (&aliens) |*alien| {
// 核心代码:调用方法
// 这行代码等价于:HeatRay.zap(heat_ray, alien)
heat_ray.zap(alien);
if (alien.health > 0) aliens_alive += 1;
}
std.debug.print("{} aliens. ", .{aliens_alive});
}
std.debug.print("Earth is saved!\n", .{});
}
核心知识点总结
1. 结构体即命名空间
在 Zig 中,struct 既是类型定义,也是命名空间。
像 Alien.hatch 这样的函数,就像是工厂方法。因为它不依赖于具体的实例,所以我们通过类型名直接调用它。
2. 语法糖:点号调用
当函数的第一个参数类型与结构体匹配时,Zig 允许我们使用 instance.method() 的写法。
- 定义:
fn zap(self: HeatRay, ...) - 调用:
heat_ray.zap(...)编译器会自动把heat_ray作为第一个参数self传进去。这让代码读起来非常自然,就像在操作一个对象一样。
3. self 只是个名字
不像 Python,Zig 没有把 self 设为关键字。你完全可以写成 fn zap(weapon: HeatRay, ...),然后用 weapon.damage。但为了社区规范和可读性,建议始终使用 self。
后续预告:我们刚才的方法是不可变的(传入的是 HeatRay 值)。如果方法需要修改结构体自身的状态怎么办?下一篇我们将学习如何传递指针给方法(Mutable Methods)。