Zig 0.17.0 已于 2026 年 10 月 2 日正式发布。本次版本历经 5 个月的开发周期,凝聚了 206 位贡献者的心血,共计包含 925 次代码提交。本次更新的重点在于构建系统的深度重构(包含独立的 Maker 进程和全新的构建服务器协议)、ELF 链接器的增强,以及多项旨在清理和规范化语言语法的破坏性变更。
笔者将在此提炼本次官方发布的重要特性与破坏性变更,帮助大家快速了解并平滑迁移到 0.17.0。
1. 增量编译 (Incremental Compilation) 走向成熟
在 0.17.0 中,编译器对增量编译的实现得到了显著改进。结合之前版本引入并持续优化的 ELF 链接器,增量编译现在可以为项目代码的修改提供近乎即时的重建速度。
解决什么问题: 缩短编译等待时间,极大提升日常开发的迭代效率。
使用方法:
目前,大多数以 x86_64-linux 为目标平台的项目已经可以直接享受此特性。在构建时传入以下参数即可让构建系统监听源码变更并执行增量重建:
zig build -fincremental --watch
2. 构建系统重构:独立的 Maker 进程与构建服务器协议
zig build 的执行流程发生了重大架构变化。
解决什么问题:
以往执行 zig build 时,构建脚本及其依赖解析混合在一起。现在,将运行项目 build.zig 代码的进程与执行包管理和构建图执行的进程分离开来。Maker 进程在首次构建后无需在未修改脚本时重复编译,且启用了优化,大幅提升了 --watch 和 --fuzz 的响应速度。
此外,引入了全新的构建服务器协议(Build Server Protocol)。通过传入 --listen=-,构建系统可以提供一个协议接口,允许 IDE 等第三方工具直接监听并控制构建图的执行。
使用示例:
# 将构建配置序列化为 .zon 格式并输出
zig build --print-configuration
# 启动构建服务器供第三方工具接入
zig build --listen=-
3. @bitCast 语义变更(破坏性变更)
在 0.17.0 中,@bitCast 内置函数的定义被更改为将值的“逻辑位表示”重新解释为不同类型,从而实现端序无关(endian-agnostic)的转换。
解决什么问题:
清理和明确位转换的边界,避免使用 extern struct 和 extern union 进行不安全的类型双关(type punning)操作。
注意点:
该变更可能在不触发编译错误的情况下改变涉及数组或向量类型的 @bitCast 行为。此外,不再允许包含 extern struct 或 extern union 的转换。如果需要对内存表示进行转换,需改用 @ptrCast 或 extern union。
官方示例代码:
const TwoBytes = extern struct {
b0: u8,
b1: u8,
};
// 在 0.17.0 中,对 extern struct 使用 @bitCast 将导致编译错误:
// error: cannot @bitCast from 'bitcast_extern_struct.TwoBytes'
test "type pun extern struct" {
const bytes: TwoBytes = .{ .b0 = 0x12, .b1 = 0xAB };
const int: u16 = @bitCast(bytes);
// ...
}
4. 数组乘法语法移除 (Array Multiplication Syntax Removed)
为了减少语言的复杂性和特殊语法,移除了数组相乘语法。
解决什么问题:
统一数据填充方式。以往使用 a ** b 语法进行数组复制填充,现在必须使用标准内置函数 @splat。
官方示例代码:
// 旧语法(0.17.0 中已移除):
// var result = [1]u8{0} ** window_size;
// 新语法:
var result: [window_size]u8 = @splat(0);
5. errdefer 捕获移除 (errdefer Capture Removed)
errdefer 语句不再支持捕获当前抛出的错误实例。
解决什么问题:
简化 errdefer 的语法语义。此前可以通过 |err| 捕获错误并处理,但这增加了控制流的复杂性。
官方示例代码:
// 旧语法(0.17.0 中已移除):
// errdefer |err| std.debug.panic("panic: {s}", .{@errorName(err)});
// 迁移方式:将需要捕获错误的逻辑拆分出内部函数,并使用 catch
fn processOneTarget(job: Job) void {
processOneTargetInner(job) catch |err| std.debug.panic("panic: {s}", .{@errorName(err)});
}
fn processOneTargetInner(job: Job) !void {
// 实际的业务逻辑
}
6. void{} 语法移除
在更严格的语法规范化进程中,无类型实例初始化的方式也被简化。
解决什么问题:
消除冗余语法。void{} 现在被视为无效语法,需使用普通的 {} 完成初始化。
官方示例代码:
// 0.17.0 之前可以使用 void{},现在必须直接使用 {}
升级建议
由于包含了不一定抛出编译错误的语义变更(如 @bitCast),本次升级需要谨慎对待。
| 适用对象 | 建议升级时间 | 升级前注意事项 |
|---|---|---|
| 新项目 | 立即升级 | 直接基于 0.17.0 新语法开发,可享受更快的增量编译速度。 |
| 个人及实验性项目 | 推荐尽快升级 | 请通过编译器报错修正 ** 及 errdefer 语法,排查 @bitCast 数组导致的隐藏逻辑问题。 |
| 企业级 / 生产环境项目 | 评估后升级 | 必须编写测试验证 @bitCast 的运行结果,清理所有 extern struct 类型双关代码并改用 @ptrCast。 |
| 使用 ZLS 的开发者 | 关注适配情况 | Maker 进程分离属于破坏性变更,ZLS 已在适配最新的构建服务器协议,升级初期部分补全功能可能受限。 |
参考来源: