- 引言 — 8 个月、1183 次提交,还有这次翻新了 stdlib 的发布
- 把 Io 当参数传 — Allocator 走过的老路
- 怎么读实现清单 — 什么是完成,什么是实验
- async 承诺了什么,又没承诺什么
- 取消,才是真正难的部分
- 「Juicy Main」— main 创建 Io
- 函数颜色问题解决了吗 — 发布说明从没这么说过
- 编译器这边 — 增量编译与新 ELF 链接器
- 后端现状 — x86 是默认项,aarch64 停了下来
- 升级的代价 — 什么会坏
- 那么,现在该不该用 Zig
- 结语
- 参考资料
引言 — 8 个月、1183 次提交,还有这次翻新了 stdlib 的发布
Zig 0.16.0 已于 2026 年 4 月 14 日发布。官方公告很简短 — 8 个月的工作、244 位贡献者、1183 次提交。(下载索引里记录的 tarball 日期早一天,是 2026-04-13。)发布说明自己挑出来的唯一标题是「I/O as an Interface」。
用一句话概括就是这样。照搬发布说明的原话,从 Zig 0.16.0 起,所有输入输出功能都必须以 Io 实例作为参数。不管是读文件、写 socket、睡眠,还是往标准输出打一行字,都一样。判断标准也写在说明里 — 凡是可能阻塞控制流、或者会引入不确定性的东西,大体上都应该归 I/O 接口所有。
这个博客前不久写过Hashimoto 为什么用 Zig 做 Ghostty。他在那篇里说,0.15 改动了「输出接口,以及一切输出东西的代码」,而且他反而很欢迎这个破坏性改动。0.16.0 是那个故事的下一章,而且是大得多的一章。这一次不只是输出,而是整个输入输出。
本文读的是发布说明,不是宣传文案。跟着它走一遍,看看什么真正完成了,什么被标成「实验性」,哪些数字是在什么条件下测出来的。先说结论 — 设计很有意思,账单也很大。
把 Io 当参数传 — Allocator 走过的老路
对稍微用过 Zig 的人来说,这个设计一点都不陌生。Zig 没有全局分配器。需要内存的函数会把 Allocator 当参数接收,具体用什么去填它,由调用者决定。库不需要知道自己是跑在 arena 上还是 GPA 上。
0.16.0 把同一招用在了 I/O 上。需要 I/O 的函数把 Io 当参数接收。而这个 Io 到底是线程池、事件循环,还是 io_uring,由调用者 — 准确地说,是应用程序的 main — 来决定。
发布说明里的 HTTP 示例很好地展示了这个设计的野心。这段代码看起来平平无奇,就是给一个域名发一个 HEAD 请求。
const std = @import("std");
const Io = std.Io;
pub fn main(init: std.process.Init) !void {
const gpa = init.gpa;
const io = init.io;
const args = try init.minimal.args.toSlice(init.arena.allocator());
const host_name: Io.net.HostName = try .init(args[1]);
var http_client: std.http.Client = .{ .allocator = gpa, .io = io };
defer http_client.deinit();
var request = try http_client.request(.HEAD, .{
.scheme = "http",
.host = .{ .percent_encoded = host_name.bytes },
.port = 80,
.path = .{ .percent_encoded = "/" },
}, .{});
defer request.deinit();
try request.sendBodiless();
var redirect_buffer: [1024]u8 = undefined;
const response = try request.receiveHead(&redirect_buffer);
std.log.info("received {d} {s}", .{ response.head.status, response.head.reason });
}
说明称,这段代码 — 一行都没多写 — 具备以下性质:它会异步地向每个配置好的域名服务器发送 DNS 查询,一有响应返回就立刻异步尝试对那个 IP 建立 TCP 连接,一旦第一个 TCP 连接成功,就把其他正在进行的连接尝试全部取消,连 DNS 查询也不例外。在 Windows 上,这一切都不依赖 ws2_32.dll。
真正有意思的是接下来这句。说明写道,这段代码用 -fsingle-threaded 编译也能跑 — 只是那时候这些操作会变成顺序执行。同一份源代码,不同的执行模型,行为依然正确。这就是这个设计在卖的东西。
怎么读实现清单 — 什么是完成,什么是实验
接口只有配上实现才有意义。而这份清单,正是 0.16.0 的宣传与现实分道扬镳的地方。照搬说明里的措辞:
Io.Threaded— 基于线程。文件系统操作直接调用 read、write、open、close。从 0.15.x 迁移代码时,这是给出等价行为的实现。用说明的话说,它功能完整、经过充分测试,也支持取消。同时它也是「Juicy Main」目前选用的实现。Io.Evented— 照搬说明的原话,仍在开发中、属于实验性,目的是探索这个接口能往哪个方向演进。基于用户态的栈切换和工作窃取 — 也就是人们说的 M:N 线程、绿色线程,或者栈式协程(stackful coroutine)。Io.Uring— 说明写明,这不是本次发布周期的重点。目前处于概念验证水平,缺网络处理、错误处理和测试覆盖。Io.Kqueue— 仅供概念验证。Io.Dispatch— 基于 macOS 的 Grand Central Dispatch。Io.failing— 模拟一个不支持任何操作的系统。
老老实实读完这份清单,结论只有一个。0.16.0 真正送进生产环境的,只有基于线程的 I/O 这一个实现,而人们谈起这次发布时真正兴奋的那个东西 — 跑在 io_uring 上的 Zig 异步 — 还没有交付。接口目前只是搭好了将来要装下它的形状。
这个区分不是在冷嘲热讽。恰恰相反,Zig 团队愿意在自己的发布说明里这么直白地写出来,这一点反倒令人印象深刻。很多项目会把概念验证叫做「支持」。
async 承诺了什么,又没承诺什么
io.async 会产出一个 Future(T),这里的 T 是被调用函数的返回类型。说明所描述的语义微妙而重要。
io.async 表达的是异步性 — 表明这次函数调用与其他逻辑相互独立。正因如此,创建这个任务不可能失败,而且即便是在完全没有并发机制的受限 Io 实现上,它也是可移植的。说明在这里补了一句相当坦诚的话 — Io 实现完全可以合法地把 async 调用实现成直接调用函数再返回。
这句话值得细品。io.async 不是「请并发执行」的指令,而是「这是独立的」这样一个声明。到底要不要真的并发运行,由实现来决定。这也正是为什么代码在 -fsingle-threaded 构建下依然能编译、依然行为正确 — 只是变成了顺序执行而已。
真正需要并发的时候,另有一个 io.concurrent。用说明的话说,它传达的是「为了正确性,必须并发执行」。而这必然要求内存分配 — 说明解释说,并发地做某件事,本质上就是这样 — 所以它可能失败。error.ConcurrencyUnavailable 就是这个失败。
也就是说,Zig 在类型系统层面,把「可以是异步的」和「必须是并发的」拆成了两个不同的函数。前者可以无限移植、不会失败;后者需要资源、可能失败。这是大多数 async 运行时都会模糊掉的区分,我个人认为这是这次发布里设计得最好的部分。
Future 有两个方法。await 在逻辑上阻塞控制流,直到任务结束并返回结果。cancel 和 await 类似,但还会要求实现中止这个操作,并返回 error.Canceled。
当多个任务的生命周期一致时,可以用 Group,说明写道,启动 N 个任务的开销是 O(1)。除此之外,一并加入的还有 Queue(T)、Select、Batch,以及在类型层面强制单位的 Clock、Duration、Timestamp、Timeout。
取消,才是真正难的部分
做过异步运行时的人都知道,难的从来不是把任务发射出去,而是把它杀掉。0.16.0 在这上面花了不少笔墨。
最让人印象深刻的实现细节是这个。据说明所述,就连 Io.Threaded 也支持取消,做法是给线程发信号,让阻塞中的系统调用返回 EINTR,再对这个错误码做出反应,在重试系统调用之前检查是否有取消请求。在一个既没有事件循环、也没有协程的普通线程池里实现取消,这个办法相当机灵。
与此同时,说明也附上了一条重要的提醒 — 就算请求了取消,这个请求也未必会被接受。取消是一个请求,不是一道命令。只有被接受的请求,才会让 I/O 操作返回 error.Canceled。
处理 error.Canceled 的方式被归纳成了三种,按常见程度排列 — 原样向外传播;调用 io.recancel() 重新武装取消请求,然后不再传播;或者用 io.swapCancelProtection() 让它变得不可达。说明还附带了一条规则 — 只有自己发起了取消请求的逻辑,才可以安全地忽略 error.Canceled。
避免资源泄漏的写法,说明也直接给出了示范。
var foo_future = io.async(foo, .{args});
defer if (foo_future.cancel(io)) |resource| resource.deinit() else |_| {}
var bar_future = io.async(bar, .{args});
defer if (bar_future.cancel(io)) |resource| resource.deinit() else |_| {}
const foo_result = try foo_future.await(io);
const bar_result = try bar_future.await(io);
说明解释了为什么 cancel 是必须的 — 因为在返回错误(包括 error.Canceled)的时候,正是这个调用负责释放异步任务的资源。也就是说,defer cancel 不是为了取消,而是为了回收资源,这是初次接触 Zig 的人一定会踩上的一颗地雷。
「Juicy Main」— main 创建 Io
既然 Io 是个参数,那第一个 Io 由谁来创建?0.16.0 的答案是 main。
const std = @import("std");
pub fn main(init: std.process.Init) !void {
const gpa = init.gpa;
const io = init.io;
const ptr = try gpa.create(i32);
defer gpa.destroy(ptr);
try std.Io.File.stdout().writeStreamingAll(io, "Hello, world!\n");
}
给 main 加上 std.process.Init 参数,就会带来一批已经初始化好的东西 — 一个贯穿进程整个生命周期的 arena、一个默认选定的通用分配器、一个根据目标配置挑好的默认 Io 实现、环境变量映射,以及给 WASI 用的 preopens。说明写道,在 Debug 模式下,条件允许时它甚至会顺带设好泄漏检测。
main 的第一个参数可以是三者之一 — 不写(那样就无法访问 CLI 参数和环境变量)、process.Init.Minimal(只给原始的 argv 和 environ),或者 process.Init。
环境变量为什么会挪到这里,说明也做了解释,而且相当有说服力。把环境当成全局状态,是一个极其常见但问题不少的抽象 — 在多线程环境下调用类似 C 的 setenv 这样的函数是不安全的,因为 environ 常常在没有任何锁的情况下被直接访问。除此之外,Zig 标准库本身还有一个大坑 — std.os.environ 原本是打算等价于 C 的 environ,但在不链接 libc 的库里,根本没办法把它填上。
在没有 Io 实例的地方临时凑一个出来的办法,说明也给了,附带的警告很坦诚。
var threaded: Io.Threaded = .init_single_threaded;
const io = threaded.io();
说明说,只要不需要任务级别的并发,这样做是能跑的,但不是理想的权宜之计。这个比喻很贴切 — 就像明明需要一个 Allocator 却没有,于是随手抓来 std.heap.page_allocator 顶上。测试里则建议像用 std.testing.allocator 那样,用 std.testing.io。
函数颜色问题解决了吗 — 发布说明从没这么说过
围绕这次发布流传最广的一句总结是「Zig 解决了函数颜色问题」。所以我去核实了一下。在 0.16.0 发布说明全文里,「color」这个词一次都没出现过。
那句话另有出处。是 Zig 核心团队的 Loris Cro 在 2025 年 7 月 13 日写的Zig's New Async I/O。他在那篇文章里分三步把论点往上垒 — 一个库可以在同步模式和异步模式下都表现最优;以前哪怕源代码摆脱了 async/await 的传染性,运行时依然被强制用无栈协程,但现在 io.async 和 Future.await 可以配合多种执行模型使用;所以 — 照他原话 — 靠着这最后一项改进,Zig 彻底打败了函数颜色问题。
这里需要加一条限定。那篇文章不是在讲 0.16.0,而是在讲当时的计划,写于 2025 年 7 月,而且它自己也写明标准库的重写工作还没完成,0.15.0 里只落地了其中很小一部分。而计划后来确实变了。Cro 提到的无栈协程,在 0.16.0 的说明里一次都没出现;真正落地的 Io.Evented,用的是栈式协程。
也有相反的意见。一位叫 ivnj 的作者,在同一天(2025 年 7 月 13 日)写的文章论点是这样的 — 从语义上说,给每个函数都传 std.Io,和把每个 Node.js 函数都变成 async、返回 Promise 没有本质区别,Zig 只是把函数颜色的选择,从阻塞/非阻塞挪到了 io/non-io。做 I/O 的函数需要 std.Io 参数,而这样的函数只能被同样接收 std.Io 的函数调用,于是这个参数不可避免地会沿着整条调用链蔓延开去。
我自己的读法是这样。两种说法都对,只是在说不同的事。颜色的传染性还在 — Io 会沿着调用链一路往上传。消失的是颜色的僵化性。在 Rust 或 JavaScript 里,函数的颜色在编译期就钉死了执行模型。而在 Zig 里,接收 Io 的函数并不钉死执行模型 — 由调用者之后再来挑,就算挑了一个完全没有并发能力的实现,代码依然是对的。而且,Allocator 会沿着调用链往上传这件事,Zig 开发者们本来就已经接受并习惯了。这不是多出来一种新颜色,而是在原本已有的颜色旁边,又贴上了一种同类的颜色。
不过这不是免费的。说明里「Lazy Field Analysis」这一条,就露出了这份代价的一角 — 这是把 I/O 变成接口之后才发现的问题:以任何形式使用 std.Io.Writer,都会把 std.Io 的 vtable 一并带进来,某些情况下会导致不必要的代码生成,从而让二进制体积膨胀。0.16.0 通过延迟类型解析修好了这个问题,但接口本身自带 vtable 值这个事实并没有消失。
编译器这边 — 增量编译与新 ELF 链接器
I/O 拿走了标题,但编译器这边的变化,可能对实务更直接有用。
增量编译这次改善很大。说明举的例子很有冲击力 — 在 Zig 编译器自身上使用增量编译时,以前那种要重新编译几乎整个编译器的改动,现在几毫秒就结束了。功劳记在「Reworked Type Resolution」头上。重做类型解析之后,编译器内部的依赖图(除了依赖环的情形)变成了无环图,也因此,「过度分析」— 重新构建超出必要范围的代码 — 在大多数情况下消失了。
增量构建会报出非增量构建里没有的「dependency loop」编译错误,这个问题这次也解决了。说明写道,这是以往版本里增量与非增量之间最大的不一致之处。LLVM 后端现在也支持增量编译了,但附带一条坦诚的说明 — 这并不会让「LLVM Emit Object」阶段变快。那个阶段完全归 LLVM 管,Zig 能做的很有限。真正变快的是 Zig 这边生成 LLVM 位码的过程,而当存在编译错误时 — 因为一旦有错误,Emit Object 会被整个跳过 — 就连 LLVM 后端也能拿到近乎即时的反馈。
而这里有这次发布里最坦诚的一句话。增量编译目前还存在包括误编译在内的已知 bug,所以在 0.16.0 里默认依然是关闭的。 但说明紧接着下一句就建议你还是打开它,理由是用户经常会对省下的时间感到惊讶。打开的方式是 zig build -fincremental --watch。
这种矛盾该怎么理解?我是这么看的 — 如果只是拿来看编译错误反馈,风险几乎为零。误编译归根结底是生成了错误的机器码的问题,而编译失败的代码根本没有机器码。但如果把增量构建出来的二进制原样拿去测试或者发布,就有可能正面撞上说明警告过的那种误编译。这次发布里比较合理的分寸大概是「开发循环里开着,最终交付物用干净构建」。
新 ELF 链接器也来了。用 -fnew-linker 打开,或者在构建脚本里设 exe.use_new_linker = true,而在 -fincremental 配合 ELF 目标的组合下,它已经是默认项。说明给出的性能数据点,场景是这样的:构建 Zig 编译器,给某个函数改一行代码,再改一行。
场景: 构建 Zig 编译器 -> 改一行 -> 再改一行
旧链接器: 14s, 194ms, 191ms
新链接器: 14s, 65ms, 64ms (快 66%)
完全跳过链接: 14s, 62ms, 62ms (快 68%)
这张表里真正有意思的是第三行。新链接器和「直接跳过链接」之间只差 3ms。所以说明认为,暴露 -Dno-bin 这个构建步骤的理由已经不多了 — 既然编译速度上的差距可以忽略不计,那不如始终开着代码生成和链接,这样最后还能顺带拿到一个可执行文件。
代价也很明确。说明明确写道,这个新链接器相对旧链接器、相对 LLD,都称不上功能完整。举的例子很扎心 — 用它构建出来的可执行文件没有 DWARF 信息。所以旧链接器和 LLD 目前都还留着,等新链接器功能真正齐全了,才会删掉旧链接器、把 LLD 从依赖里去掉。
也就是说,66% 这个数字是带着「放弃调试信息」这个条件的。那 3ms 的差距说的是同一件事 — 链接之所以近乎免费,很大一部分原因是链接器原本该做的一部分工作,现在还没做。
后端现状 — x86 是默认项,aarch64 停了下来
自举后端的进展每次发布都会提一嘴,但 0.16.0 的现状得冷静地看。
x86 后端这次修了 11 个 bug,常量 memcpy 生成的代码也更好了。照搬说明里和 LLVM 后端对比的原话 — 这个后端通过的行为测试更多、编译速度明显更快、调试信息更出色,但机器码质量更差。它依然是 Debug 模式下的默认项。
这四条恰好解释了为什么它只用在 Debug 模式。在开发循环里,编译速度和调试信息就是一切,机器码质量几乎无关紧要。到了发布构建,情况正好相反。作为参照,LLVM 后端在 2010 项行为测试里通过了 2004 项(说明把它四舍五入写成了 100%)。x86 后端通过的比这还多。
aarch64 后端是个坏消息。照搬说明的原话 — 它依然在开发中,这次发布周期里因为「I/O as an Interface」的连带影响,进度完全停滞了,现在跑行为测试会崩溃。说明写道,等标准库的变动稳定下来,预计进度会重新推进。
这直接体现了这次发布的机会成本。I/O 接口不是白来的 — 它吃掉了 aarch64 自举后端整整一个周期。对于那些在 Apple Silicon 或者 ARM 服务器上等自举后端快速编译的人来说,0.16.0 不是进步,而是停摆。WebAssembly 后端在 1970 项行为测试里通过了 1813 项(92%)。
工具链这边也有一处让人难受的地方。0.16.0 升级到了 LLVM 21.1.0,但同时把循环向量化整个关掉了。 原因很严重 — 那个回归会在常见配置下把编译器自身编译错。关掉某个特定 CPU 特性的绕过办法被认为太脆弱,所以决定在升级到修好这个 bug 的 LLVM 版本之前,干脆把向量化整个关掉。说明承认这在部分情况下会让代码生成变差,但认为总比误编译好。而且这个性能回归预计不只影响 0.16.x,还会影响 0.17.x,要到 0.18.x 才会解决。也就是说,升级到 0.16.0,就意味着要背着这个性能惩罚跨过两个版本。
也有好消息。translate-c 从依赖 libclang 改成了基于 arocc,编译器源码树里少了 5,940 行 C++(还剩 3,763 行)。这是把对 LLVM 的库依赖,逐步变成对 Clang 的进程依赖这条路上的一步。遍历切片的 for 循环的安全检查也做了改进,生成的代码少了大约 30%;新加入的 deflate 压缩,在默认压缩级别下,墙钟时间比 zlib 快 9.7% ± 0.2%(不过 zlib 的压缩率好 1.00%,指令数反而多 18.9% — 这是缓存缺失和分支预测失误减少之后,时间上赢回来的一个例子)。
升级的代价 — 什么会坏
「所有输入输出都接收 Io」这句话,从升级的角度看,等价于「所有做输入输出的代码都会坏」。
先看被删掉的东西。Io.GenericWriter、Io.AnyWriter、Io.null_writer、Io.CountingReader 都没了。SegmentedList、meta.declList、Thread.Mutex.Recursive 也消失了。格式化 API 变了 — fmt.format 变成了 std.Io.Writer.print,Formatter 变成了 Alt,FormatOptions 变成了 Options,bufPrintZ 变成了 bufPrintSentinel。
错误集也做了整理。error.RenameAcrossMountPoints 和 error.NotSameFileSystem 合并成了 error.CrossDevice,error.SharingViolation 变成了 error.FileBusy,error.EnvironmentVariableNotFound 变成了 error.EnvironmentVariableMissing。DynLib 去掉了 Windows 支持,说明里的吐槽很好笑 — 现在用户得自己直接用 LoadLibraryExW 和 GetProcAddress,反正大家可能早就已经这么干了。
语言本身也在动。@Type 被拆成了几个各自独立的类型生成内建函数,@cImport 正在往构建系统迁移(内建函数暂时还留着,但背后由 Aro 来撑),运行时向量索引被禁止了,packed struct 和 union 里也不能再有指针。「Reworked Type Resolution」整体上变得更宽松了,但不是严格意义上更宽松 — 一些以前能编译通过的代码,现在会报依赖环错误。
写到这儿,又想起Ghostty 那篇。Hashimoto 说 0.15 改动了「一切输出东西的代码」时,那不是终点,而是起点。0.16.0 把 writer 那一整套又翻新了一遍。他欢迎这种做法的理由 — Andrew Kelley 一旦判断某个改动是必要的,就会不回头地推下去 — 在这里再一次得到了印证。而能不能欢迎这种态度,才是判断能不能用这门语言的真正标准。
发布说明里专门有一节,标题就叫「This Release Contains Bugs」。这个周期关闭了 345 个 bug 报告,但说明写道,Zig 依然存在已知 bug、误编译和回归。还有这句关键的话 — 哪怕在 0.16.x,用 Zig 做一个不算小的项目,也可能需要你参与到开发过程里去。 说明还补充说,等到 1.0.0 落地,才会给 Tier 1 支持加上 bug 政策这个额外要求。反过来说,现在还没有这个政策。
那么,现在该不该用 Zig
归纳一下,判断标准可以这样分。
说得通的情况
- 你在做工具或基础设施代码,团队有余力吸收工具链的破坏性改动 — 升级是日常,不是一次事件的组织。
- 你确实需要一个能被注入的 I/O 抽象 — 同一个库,在 CLI 里跑单线程,在服务器上跑线程池。这是 0.16.0 真正交付的东西。
- 交叉编译和 C 互操作是核心需求。Zig 在这方面的优势,跟 0.16.0 这些动静没关系,一直都在。
- 编译速度是开发体验的瓶颈,光是
-fincremental --watch带来的快速错误反馈就已经值回票价。
还不是时候
- 你需要稳定的 API。0.15 改了 writer,0.16 改了整个 I/O。没有人承诺 0.17 会改什么。在 1.0 之前,指望 Zig「这次是最后一次大改」,没有任何依据。
- 你现在就需要跑在 io_uring 上的生产级异步 I/O。0.16.0 给的是概念验证 — 网络处理、错误处理、测试覆盖都还没有。
- 你需要 aarch64 上自举后端的快速编译。这个周期进度停了,现在跑行为测试会崩溃。
- 发布构建的机器码质量是第一优先级。自举 x86 后端机器码质量差,这是说明自己写的,而且 LLVM 循环向量化要关到 0.17.x。
- 团队承担不起「碰到语言 bug 就要参与上游」这件事。这不是我的判断,是发布说明里白纸黑字的警告。
结语
0.16.0 让我学到的东西,与其说是关于这门语言,不如说是关于设计的。
Zig 把 Allocator 当参数传递的这个习惯,原本讲的从来就不是内存本身,而是「策略由调用者决定,库只提供机制」这件事。0.16.0 把这个原则推到了 I/O 上,也在这个过程里,把大多数 async 运行时都会模糊掉的那个区分 — 可以是异步的,和必须是并发的 — 拆成了 io.async 和 io.concurrent。把取消当成请求而不是命令来处理,在线程池里用信号和 EINTR 实现取消,这些做法透出的都是同一种坦诚。
函数颜色问题算不算被「打败」,我觉得取决于怎么定义 — Io 依然会沿着调用链一路往上传。但传上去的是选择权,而不是执行模型,这一点上,我认为这是个有实质意义的差别。而发布说明自己从没宣称过这个胜利 — 那句话是核心团队成员写在个人博客里的,不在官方说明里 — 这一点反而让我对这个团队更信任。
与此同时,账单也是真实的。完成的实现只有一个,aarch64 停了,增量编译因为误编译默认关闭,新链接器生成不了 DWARF,LLVM 向量化要关两个版本,凡是碰 I/O 的代码都得重写。发布说明对这些一个都没藏着掖着 — 事实上,我在这篇文章里写的负面事实,几乎全部原样来自说明本身。
这大概就是对这次发布最准确的概括。Zig 依然不是一件「完成品」,而是一场「进行中的论证」,发布说明就是那场论证的会议纪要。如果你愿意参与这场讨论,0.16.0 是一次有意思的发布。如果你是来买产品的,请先读一下说明最底部「This Release Contains Bugs」那一节。那一节存在的意义,不是走个免责的形式,而是对这门语言此刻真实处境的准确陈述。
参考资料
- Zig 0.16.0 Release Notes — 官方发布说明(本文几乎所有事实和数字的出处)
- 0.16.0 Released — 官方公告(2026 年 4 月 14 日,244 位贡献者,1183 次提交)
- ziglang.org 下载索引 — 各版本发布日期(0.16.0: 2026-04-13)
- Zig's New Async I/O — Loris Cro,2025 年 7 月 13 日(「彻底打败函数颜色问题」的出处,基于当时的计划)
- Zig's new I/O: function coloring is inevitable? — ivnj,2025 年 7 月 13 日(相反的论点)
- 重返终端的创业者 — Hashimoto 的 Ghostty、Zig 这场豪赌,以及"没有义务"的开源(相关文章)
현재 단락 (1/129)
Zig 0.16.0 [已于 2026 年 4 月 14 日发布](https://ziglang.org/news/0.16.0-released/)。官方公告很简短 — 8 个月的工作、244 位...