|
| 1 | +--- |
| 2 | +outline: deep |
| 3 | +comments: false |
| 4 | +showVersion: false |
| 5 | +--- |
| 6 | + |
| 7 | +# `0.16.0` |
| 8 | + |
| 9 | +2026/4/13,`0.16.0` 发布,历时 8 个月,有 244 位贡献者参与,一共进行了 1183 次提交! |
| 10 | + |
| 11 | +如果要用一句话概括这个版本,那就是:**`0.16.0` 把上一轮预告过的大量基础设施重构真正落地了。** |
| 12 | + |
| 13 | +`0.15.x` 还在为 `std.Io`、增量编译和新的工具链架构铺路,到了 `0.16.0`,这些方向已经进入可以大规模体验的阶段:I/O 统一为接口、`main` 可以直接拿到 `io` 和 `gpa`、增量编译进一步可用、新 ELF linker 开始接入默认流程,同时语言层也继续清理历史设计。 |
| 14 | + |
| 15 | +## 目标支持 |
| 16 | + |
| 17 | +`0.16.0` 在目标支持上的一个重要变化,是 Zig 对“哪些平台值得持续投入工程质量”这件事变得更明确了。 |
| 18 | + |
| 19 | +比较值得注意的点有: |
| 20 | + |
| 21 | +- `aarch64-freebsd`、`aarch64-netbsd`、`loongarch64-linux`、`powerpc64le-linux`、`s390x-linux`、`x86_64-freebsd`、`x86_64-netbsd`、`x86_64-openbsd` 这些目标现在都会在 Zig 的 CI 中原生测试 |
| 22 | +- 新增 `aarch64-maccatalyst` 与 `x86_64-maccatalyst` 的交叉编译支持 |
| 23 | +- 新增 `loongarch32-linux` 的初始支持,不过当前仍不支持 libc |
| 24 | +- Alpha、KVX、MicroBlaze、OpenRISC、PA-RISC、SuperH 等架构加入了基础支持 |
| 25 | +- Oracle Solaris、IBM AIX、z/OS 支持被移除;`illumos` 不受影响,仍然保留支持 |
| 26 | +- 栈回溯支持进一步扩大,几乎所有主流目标在崩溃时都能得到更可靠的 stack trace |
| 27 | + |
| 28 | +对普通用户来说,这意味着 Zig 在常见 Linux / BSD / macOS / Windows 目标上的“可用性底线”又往前推了一步;而对于比较边缘的平台,官方也更清晰地区分了“支持”“实验性支持”和“不再支持”。 |
| 29 | + |
| 30 | +## 系统最低版本要求 |
| 31 | + |
| 32 | +| 操作系统(Operating System) | 最低版本要求(Minimum Version) | |
| 33 | +| :--------------------------- | :-----------------------------: | |
| 34 | +| DragonFly BSD | 6.0 | |
| 35 | +| FreeBSD | 14.0 | |
| 36 | +| Linux | 5.10 | |
| 37 | +| NetBSD | 10.1 | |
| 38 | +| OpenBSD | 7.8 | |
| 39 | +| macOS | 13.0 | |
| 40 | +| Windows | 10 | |
| 41 | + |
| 42 | +## 语言变动 |
| 43 | + |
| 44 | +### `switch` 继续补齐语义 |
| 45 | + |
| 46 | +`switch` 是这一轮里继续被打磨的语言特性之一。现在,`packed struct` 和 `packed union` 可以直接作为 prong item,比较规则按照 backing integer 来做;同时,decl literals、需要结果类型的表达式、union tag capture 等场景也获得了更一致的支持。 |
| 47 | + |
| 48 | +这类变更本身并不一定会让旧代码报错,但它明显减少了过去一些“语言明明应该支持、但实现上还没补齐”的边角问题。 |
| 49 | + |
| 50 | +### `@cImport` 正式进入“迁移期” |
| 51 | + |
| 52 | +`0.16.0` 仍然保留了 `@cImport`,但已经明确将其标记为 deprecated。官方方向是把 C 头文件翻译迁到构建系统中,通过 `build.zig` 里的 `addTranslateC` 生成模块,再在 Zig 代码里使用 `@import("c")`。 |
| 53 | + |
| 54 | +这和 Zig 未来“逐步把对 LLVM / Clang 的库级依赖转向进程级依赖”的方向是一致的。 |
| 55 | + |
| 56 | +同时,`translate-c` 的实现现在已经从 `libclang` 切换到了 Aro / translate-c 方案。对大多数用户来说这是透明的,但如果你升级后发现 C 头文件翻译行为有差异,它更可能是实现 bug,而不是新的预期行为。 |
| 57 | + |
| 58 | +### `@Type` 被拆分为多个独立内建函数 |
| 59 | + |
| 60 | +这是 `0.16.0` 最明显的语言级 breaking change 之一。`@Type` 被移除,原来依赖它造类型的元编程代码,需要迁移到新的内建函数: |
| 61 | + |
| 62 | +- `@EnumLiteral()` |
| 63 | +- `@Int()` |
| 64 | +- `@Tuple()` |
| 65 | +- `@Pointer()` |
| 66 | +- `@Fn()` |
| 67 | +- `@Struct()` |
| 68 | +- `@Union()` |
| 69 | +- `@Enum()` |
| 70 | + |
| 71 | +这项改动的核心目标,是让“构造类型”这件事更直观,也让常见场景不必再绕一层 `std.meta.Int`、`std.meta.Tuple` 之类的辅助函数。 |
| 72 | + |
| 73 | +### packed / extern 相关规则更严格 |
| 74 | + |
| 75 | +这次发布继续收紧了位级布局和 ABI 边界的隐式行为: |
| 76 | + |
| 77 | +- `packed union` 现在要求更明确的 backing integer 语义 |
| 78 | +- `packed struct` / `packed union` 不再允许直接放指针字段 |
| 79 | +- `extern` 场景下,`enum` 与 `packed` 类型不能再依赖隐式推断的底层整数类型 |
| 80 | + |
| 81 | +从设计上看,这些限制的方向非常统一:**凡是会影响 ABI 或精确内存布局的内容,Zig 都更倾向于要求你显式写出来。** |
| 82 | + |
| 83 | +### 向量语义进一步收紧 |
| 84 | + |
| 85 | +`0.16.0` 禁止了运行时向量索引,同时也不再鼓励通过旧式内存强转在数组和向量之间来回转换。简单来说,向量更明确地被当成“值语义上的 SIMD 数据”,而不是“碰巧可以按数组方式随便访问的内存”。 |
| 86 | + |
| 87 | +另外,小整数类型在“绝对不会丢精度”的前提下,现在可以安全地隐式转换为浮点类型,这也让数值代码更顺手了一些。 |
| 88 | + |
| 89 | +### 类型解析与依赖环错误大幅重做 |
| 90 | + |
| 91 | +`0.16.0` 还重做了编译器内部的类型解析流程。这个改动的影响非常深: |
| 92 | + |
| 93 | +- 许多以前会误报 dependency loop 的代码现在可以正常工作 |
| 94 | +- 增量编译和普通编译之间的一致性明显增强 |
| 95 | +- 一小部分本来就存在真实依赖环的代码,现在会更早、更明确地报错 |
| 96 | + |
| 97 | +如果你升级后遇到以前没见过的 dependency loop,先别急着回退版本。因为 `0.16.0` 的错误报告已经能更清楚地指出环路是怎么形成的,通常只要打断其中一条依赖即可。 |
| 98 | + |
| 99 | +## 标准库 |
| 100 | + |
| 101 | +### I/O 作为 Interface |
| 102 | + |
| 103 | +这是 `0.16.0` 最重头的内容,没有之一。 |
| 104 | + |
| 105 | +从这个版本开始,所有输入输出相关能力都围绕 `std.Io` 展开。更准确地说,凡是可能阻塞控制流,或会引入非确定性的操作,都被纳入了 `Io` 的抽象边界内。 |
| 106 | + |
| 107 | +当前官方提供了几种典型实现: |
| 108 | + |
| 109 | +- `Io.Threaded`:基于线程,功能最完整,也是从 `0.15.x` 升级时最接近旧行为的实现 |
| 110 | +- `Io.Evented`:仍在实验阶段,用来推动接口演进 |
| 111 | +- `Io.failing`:用于模拟“不支持任何操作”的环境 |
| 112 | + |
| 113 | +围绕它,标准库引入了整套新的任务和并发抽象: |
| 114 | + |
| 115 | +- `Future` |
| 116 | +- `Group` |
| 117 | +- `Batch` |
| 118 | +- `Select` |
| 119 | +- `Queue(T)` |
| 120 | +- 统一的 cancelation 模型 |
| 121 | + |
| 122 | +这不仅是 API 改名,而是 Zig 对“并发 I/O 应该怎样进入语言生态”给出的新答案。文件系统、网络、进程、同步原语、定时器等能力,都围绕这套接口重新组织了。 |
| 123 | + |
| 124 | +### `Juicy Main` |
| 125 | + |
| 126 | +为了配合新的 `std.Io`,`main` 也获得了一个很实用的新入口:`std.process.Init`。 |
| 127 | + |
| 128 | +只要把 `main` 写成: |
| 129 | + |
| 130 | +```zig |
| 131 | +const std = @import("std"); |
| 132 | +
|
| 133 | +pub fn main(init: std.process.Init) !void { |
| 134 | + const gpa = init.gpa; |
| 135 | + const io = init.io; |
| 136 | + _ = gpa; |
| 137 | + _ = io; |
| 138 | +} |
| 139 | +``` |
| 140 | + |
| 141 | +你就能直接拿到: |
| 142 | + |
| 143 | +- `gpa` |
| 144 | +- `io` |
| 145 | +- `arena` |
| 146 | +- `environ_map` |
| 147 | +- `preopens` |
| 148 | +- `minimal.args` / `minimal.environ` |
| 149 | + |
| 150 | +这让应用入口第一次真正成了“进程上下文的注入点”。对于应用开发来说,这个改动的体感甚至不亚于 `std.Io` 本身。 |
| 151 | + |
| 152 | +### 环境变量和进程参数不再是全局状态 |
| 153 | + |
| 154 | +和 `Juicy Main` 配套的另一项重要变化,是环境变量和进程参数都不再被鼓励当成全局状态来访问。 |
| 155 | + |
| 156 | +现在,环境变量原则上只存在于应用入口的 `Init` 里;需要使用它们的函数,应当显式接收需要的值,或者接收 `*const std.process.Environ.Map`。 |
| 157 | + |
| 158 | +这个方向很符合 Zig 一贯的设计哲学:尽量少依赖隐式的全局上下文,让副作用和依赖关系都显式体现在函数签名里。 |
| 159 | + |
| 160 | +### 线程与分配器模型继续更新 |
| 161 | + |
| 162 | +围绕新的 `std.Io`,标准库的并发相关设施也继续收敛: |
| 163 | + |
| 164 | +- `std.Thread.Pool` 被移除,官方建议迁移到 `std.Io.async` / `std.Io.Group.async` |
| 165 | +- `std.heap.ArenaAllocator` 变成了 thread-safe 且 lock-free |
| 166 | +- `std.heap.ThreadSafeAllocator` 被移除 |
| 167 | + |
| 168 | +如果把这些变化放在一起看,会发现 Zig 正在逐步放弃一些“靠包装器补线程安全”的旧路子,转而更偏向于:让真正需要并发的基础组件自己具备合适的并发语义。 |
| 169 | + |
| 170 | +### 文件系统、路径与容器 API 持续整理 |
| 171 | + |
| 172 | +除了 `std.Io` 大迁移之外,这次标准库还有很多看起来零碎、但真实影响升级体验的整理工作: |
| 173 | + |
| 174 | +- `std.io` 继续收敛到 `std.Io` |
| 175 | +- `std.fs` 的一批常用入口迁到 `std.Io.Dir` / `std.Io.File` |
| 176 | +- `std.process.getCwd*` 改名为 `currentPath*` |
| 177 | +- `fs.path.relative` 变成纯函数,需要显式传入上下文 |
| 178 | +- `File.Stat.atime` 变成可选值 |
| 179 | +- `std.mem` 里 “index of” 系列统一更名为 “find” |
| 180 | +- 一批容器继续向 unmanaged 方向迁移,`PriorityQueue` / `PriorityDequeue` 的命名也更统一了 |
| 181 | + |
| 182 | +这些调整单看都不算大新闻,但合在一起,就是一次很典型的 Zig 式“去历史包袱”整理。 |
| 183 | + |
| 184 | +### Windows 标准库实现继续下沉 |
| 185 | + |
| 186 | +Windows 也是 `0.16.0` 里非常有意思的一条线: |
| 187 | + |
| 188 | +- 网络 API 不再依赖 `ws2_32.dll`,而是直接基于 AFD 实现 |
| 189 | +- 标准库继续向 NtDll 收敛 |
| 190 | +- `std.Progress` 现在也支持 Windows 下的跨进程进度上报 |
| 191 | + |
| 192 | +这些工作虽然对多数用户不可见,但会真实影响程序的健壮性、性能,以及 cancelation / batch 模型在 Windows 上的完整度。 |
| 193 | + |
| 194 | +## 构建系统 |
| 195 | + |
| 196 | +### 依赖目录改到项目本地 `zig-pkg` |
| 197 | + |
| 198 | +从 `0.16.0` 开始,依赖包会被拉取到项目根目录旁边的 `zig-pkg` 目录,而不是继续使用过去那种全局解压缓存模式。 |
| 199 | + |
| 200 | +这个变化的好处很直接: |
| 201 | + |
| 202 | +- 你可以更方便地阅读、搜索、修改依赖源码 |
| 203 | +- 可以更自然地把依赖目录换成本地 git clone |
| 204 | +- IDE 也更容易直接索引整棵依赖树 |
| 205 | + |
| 206 | +### `zig build --fork` |
| 207 | + |
| 208 | +构建系统新增了 `--fork=[path]` 参数,可以让你临时用本地目录里的 fork 覆盖依赖树中的匹配包。 |
| 209 | + |
| 210 | +这对生态 breakage 的排查非常有帮助:你可以在不改版本元数据的前提下,直接调试一整串依赖之间的兼容问题。 |
| 211 | + |
| 212 | +### 依赖元数据更严格 |
| 213 | + |
| 214 | +`0.16.0` 还提高了 `build.zig.zon` 的要求: |
| 215 | + |
| 216 | +- 缺少 `fingerprint` 会直接失败 |
| 217 | +- `name` 不能再用字符串,必须写成 enum literal |
| 218 | +- 旧 hash 格式支持已被移除 |
| 219 | + |
| 220 | +这意味着旧项目在升级时,最好顺手检查一遍所有依赖元数据,而不是等到 `zig build` 报错再逐个补。 |
| 221 | + |
| 222 | +### 新增测试超时与错误输出样式 |
| 223 | + |
| 224 | +`zig build` 新增了几项很适合日常开发的参数: |
| 225 | + |
| 226 | +- `--test-timeout` |
| 227 | +- `--error-style` |
| 228 | +- `--multiline-errors` |
| 229 | + |
| 230 | +同时,旧的 `--prominent-compile-errors` 被移除了,对应的新写法是 `--error-style minimal`。 |
| 231 | + |
| 232 | +### 临时文件 API 被重构 |
| 233 | + |
| 234 | +`Build.makeTempPath` 和 `RemoveDir` step 都被清理掉了,新的推荐路径是: |
| 235 | + |
| 236 | +- `Build.addTempFiles` |
| 237 | +- `Build.addMutateFiles` |
| 238 | +- `Build.tmpPath` |
| 239 | + |
| 240 | +这项重构背后的核心思路,是把“临时目录”“可变文件”“缓存语义”这些东西从一开始就表达清楚,而不是让旧 API 在 configure 阶段偷偷做一堆文件系统副作用。 |
| 241 | + |
| 242 | +## Compiler |
| 243 | + |
| 244 | +### `translate-c` 改用 Aro |
| 245 | + |
| 246 | +编译器内部的 `translate-c` 现在基于 Aro / translate-c,而不是 `libclang`。这使 Zig 离“摆脱对 LLVM 的库级依赖”又近了一步。 |
| 247 | + |
| 248 | +对普通用户来说,这更多体现为长期方向上的信号:Zig 仍在持续拆除自己对 LLVM 的深绑定。 |
| 249 | + |
| 250 | +### 类型解析重构 |
| 251 | + |
| 252 | +前面在“语言变动”里提过,`0.16.0` 大幅重做了类型解析。这件事对编译器本身还有两个非常重要的连锁收益: |
| 253 | + |
| 254 | +- 依赖环报错更可解释 |
| 255 | +- 增量编译和普通编译之间的一致性更强 |
| 256 | + |
| 257 | +这也是为什么你会发现,本版本许多看似分散的改动,最后都会回到“为了更可靠的增量编译”这个主题上。 |
| 258 | + |
| 259 | +### 增量编译继续前进 |
| 260 | + |
| 261 | +`0.16.0` 的增量编译已经比 `0.15.x` 实用得多: |
| 262 | + |
| 263 | +- 大多数场景下减少了“过度重编译” |
| 264 | +- LLVM 后端也开始支持增量编译 |
| 265 | +- ELF 目标在 `-fincremental` 下会默认启用新的 ELF linker |
| 266 | +- 稳定性明显提升,虽然依然不是默认开启 |
| 267 | + |
| 268 | +官方现在明确鼓励大家实际使用: |
| 269 | + |
| 270 | +```sh |
| 271 | +zig build -fincremental --watch |
| 272 | +``` |
| 273 | + |
| 274 | +当然,它仍然有已知 bug,甚至可能包含误编译;所以这项功能在 `0.16.0` 里依然不是默认值。 |
| 275 | + |
| 276 | +### 后端进展 |
| 277 | + |
| 278 | +这一轮里: |
| 279 | + |
| 280 | +- x86 自托管后端修了 11 个 bug,仍然是 Debug 模式下的默认后端 |
| 281 | +- aarch64 后端因为 `std.Io` 带来的标准库 churn 暂时放慢了节奏 |
| 282 | +- Zig 的 WebAssembly 后端目前通过了 1813 / 1970(约 92%)项行为测试 |
| 283 | + |
| 284 | +## 链接器(Linker) |
| 285 | + |
| 286 | +### 新 ELF Linker |
| 287 | + |
| 288 | +`0.16.0` 的新 ELF linker 可以通过 `-fnew-linker` 显式启用,或者在 build 脚本里设置 `exe.use_new_linker = true`。更重要的是:**在 `-fincremental` 且目标是 ELF 时,它现在会默认启用。** |
| 289 | + |
| 290 | +官方给出的一个数据点非常直观:对 Zig 编译器本体做单行改动时,旧 linker 需要大约 `194ms`,而新 linker 只需要 `65ms`,几乎接近“完全跳过链接”的速度。 |
| 291 | + |
| 292 | +这也意味着过去那种专门暴露 `-Dno-bin`、只求快速拿到编译错误的工作流,收益已经没有以前那么明显了。 |
| 293 | + |
| 294 | +不过要注意,新 linker 目前还没完全补齐旧 linker / LLD 的能力,例如生成物还缺少 DWARF 信息。所以它已经够快、够值得试,但还没有到“所有场景都能无脑切换”的程度。 |
| 295 | + |
| 296 | +## Fuzzer(模糊测试器) |
| 297 | + |
| 298 | +### `Smith` 取代 `[]const u8` |
| 299 | + |
| 300 | +Fuzz 测试接口是 `0.16.0` 里另一个会直接影响用户代码的 breaking change。过去 fuzz test 习惯接收 `[]const u8` 输入;现在统一改成 `*std.testing.Smith`,由它来生成结构化值。 |
| 301 | + |
| 302 | +这套接口不仅更适合做复杂输入生成,还支持权重、范围、哈希相关性等能力,明显比过去的“原始字节切片”模式更强。 |
| 303 | + |
| 304 | +### 多进程、多核与 crash dump |
| 305 | + |
| 306 | +除了接口变化之外,fuzzer 本身也更强了: |
| 307 | + |
| 308 | +- 现在可以利用多核,受 `-j` 控制 |
| 309 | +- 多个 fuzz test 会自动轮换并优先运行更“有产出”的测试 |
| 310 | +- 崩溃输入会自动落盘,便于复现 |
| 311 | + |
| 312 | +## Bug 修复 |
| 313 | + |
| 314 | +本轮发布周期内,Zig 一共关闭了 345 个 bug 报告。 |
| 315 | + |
| 316 | +不过官方也直说了:**这个版本仍然包含已知 bug、误编译和回归问题。** 对于稍微复杂一点的项目,使用 `0.16.x` 仍然意味着要准备好参与 issue 反馈、最小复现和版本试验。 |
| 317 | + |
| 318 | +这并不意外。因为 `0.16.0` 本质上是一个“大迁移版本”,它把很多还在演进中的长期工程方向一次性推到了用户面前。 |
| 319 | + |
| 320 | +## 工具链(Toolchain) |
| 321 | + |
| 322 | +### LLVM 21 |
| 323 | + |
| 324 | +`0.16.0` 升级到了 LLVM `21.1.0`,覆盖了 Clang、libc++、libc++abi、libunwind、libtsan 等组件。 |
| 325 | + |
| 326 | +不过这里有一个非常值得注意的 caveat:为了规避 LLVM 上游的严重回归,Zig 在 `0.16.x` 中**完全禁用了 loop vectorization**。这会让某些代码生成结果比理想情况更保守,但它仍然比“在常见配置下误编译 Zig 编译器自身”要好得多。 |
| 327 | + |
| 328 | +官方预计这个性能回退不止会影响 `0.16.x`,甚至还会延续到 `0.17.x`,大概率要等到 `0.18.x` 才会彻底解决。 |
| 329 | + |
| 330 | +### libc 与系统头文件更新 |
| 331 | + |
| 332 | +这一轮工具链同时带来了: |
| 333 | + |
| 334 | +- `musl 1.2.5`(附带安全修复回移) |
| 335 | +- `glibc 2.43` |
| 336 | +- Linux `6.19` headers |
| 337 | +- macOS `26.4` headers |
| 338 | +- FreeBSD `15.0` libc |
| 339 | +- 交叉编译时支持动态链接的 OpenBSD libc |
| 340 | + |
| 341 | +### `zig libc` 继续扩张 |
| 342 | + |
| 343 | +`zig libc` 继续吞并原来来自 musl、MinGW-w64、WASI libc 的一部分 C 实现。`0.16.0` 中,随 Zig 分发的 C 源文件总数从 `2270` 降到了 `1873`,减少了约 `17%`。 |
| 344 | + |
| 345 | +这里尤其值得一提的是:很多数学函数,以及 `malloc` 相关函数,现在都已经进入 `zig libc` 的实现范围。 |
| 346 | + |
| 347 | +### `zig cc` |
| 348 | + |
| 349 | +`zig cc` / `zig c++` 现在基于 Clang `21.1.8`。这意味着 Zig 在“C / C++ 工具链外壳”这条线上,也继续和整体 LLVM 版本一同推进。 |
| 350 | + |
| 351 | +## 路线图(Roadmap) |
| 352 | + |
| 353 | +官方对 `0.17.0` 的规划很明确:这是一个相对短周期版本,主要目标是升级到 LLVM `22`,并完成“把构建执行阶段和 `build.zig` 配置阶段分离”的工作。 |
| 354 | + |
| 355 | +在这之后,更长期的大方向仍然是: |
| 356 | + |
| 357 | +- 继续完成并稳定语言本身 |
| 358 | +- 做完 aarch64 后端,并让它成为 Debug 模式默认后端 |
| 359 | +- 继续增强链接器,减少对 LLD 的依赖,并服务增量编译 |
| 360 | +- 继续增强内置 fuzzer |
| 361 | +- 继续把对 LLVM 的依赖,从“链接库”转向“调用 Clang 进程” |
| 362 | + |
| 363 | +如果说 `0.15.x` 还是“这些方向马上就要影响到你了”,那 `0.16.0` 就是“它们已经开始真正影响你的日常开发了”。 |
0 commit comments