Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Zig Creator Andrew Kelley Talks Zig’s IO Design and Function Coloring Problem (2025-10-10, qwen3.5-397b-a17b)

1. 导读

本期播客由 Zig 语言创始人 Andrew Kelley 与 Roc 语言创始人 Richard Feldman 展开对话。时值 Zig 0.15.1 版本筹备期,且 Roc 计划将其编译器用 Zig 进行重写,双方围绕编译器架构、I/O 抽象模型、基于索引的中间表示(IR)以及热代码加载等底层工程问题进行了探讨。

对话展现了系统级语言在开发体验、执行性能与底层可控性之间的权衡,为评估底层工具链设计、I/O 抽象以及编译器吞吐量提供了具体的工程参考。

2. 核心观点

Andrew Kelley 坚持显式控制与贴近硬件的哲学,主张系统级编程语言不应过度隐藏底层细节,而应通过透明的数据布局、显式的资源传递与完备的可测试性来保障代码质量与性能。

I/O 参数化应对“函数颜色”问题 Kelley 认为,将 I/O 像 Allocator 一样作为显式参数传入,而非依赖全局状态,有助于统一同步与异步的抽象差异。调用方无需关心底层具体是阻塞还是非阻塞调用,只需传入不同的 I/O 实现。Zig 标准库的“Writergate”重构正是基于此理念,避免了维护两套割裂的同步与异步标准库。

基于索引的 IR 设计利于快速缓存与序列化 Kelley 指出,编译器中间表示(IR)若尽量用相对于基地址的数组索引代替指针,可以极大简化状态持久化。因为索引值固定不变,整个模块的 IR 状态可以通过单次系统调用(如 pwritev)直接转储至磁盘,大幅降低序列化与反序列化的开销。

基于共享内存的增量热代码重载 Kelley 提出通过命名共享内存实现编译器与运行时的协同,使解释执行器可直接在共享内存中的 IR 上运行。在代码变动时只需切换内存块而无需经历完整的重链接过程,以此规避传统链接器在增量构建中的性能损耗。

显式错误传递优于运行时崩溃 对话强调编译器应完整呈现所有潜在故障路径。Kelley 指出,若代码中广泛使用 unwrap,容易将本可捕获的错误变成难以排查的运行时崩溃。Zig 的设计将内存分配失败等场景均作为显式错误值处理,要求开发者显式应对。

结合覆盖率的原生模糊测试(Fuzzing) Kelley 认为依靠人工编写用例难以穷尽边界输入,应引入基于代码覆盖率引导的模糊测试。Zig 计划在工具链中更紧密地集成 Fuzzing 支持,利用自动化测试在代码上线前探测潜在的非法状态与崩溃风险。

这些观点体现了 Zig 的工程逻辑:通过显式参数化提升模块可测性,通过索引化数据结构优化缓存序列化,并通过底层内存控制加速构建反馈循环。

3. 批判与质疑

Kelley 的工程构想追求性能与控制,但在实际落地中也面临若干客观挑战:

首先,共享内存机制在受限环境中的兼容性。 在部分强管控的企业桌面或受限生产环境中,杀毒软件与终端安全防护工具往往对跨进程共享内存修改高度敏感。若机制被安全策略频繁拦截或误报,该热加载方案的通用性将受到制约。

其次,指针修复(Pointer Fixup)的人工维护负担。 尽管索引化大幅减少了指针数量,但反序列化时剩余关键指针的人工修复仍需高度谨慎。一旦在极端边界下遗漏修复,可能引发难以定位的内存段错误。

最后,I/O 参数显式透传的样板代码开销。 将 I/O 实现逐层显式透传虽然增强了纯度与可测性,但在深层业务调用链路中,可能导致函数签名变得冗长复杂,对大型复杂软件工程的易用性构成考验。

4. 行业视野

这场讨论反映了当前系统编程与编译器工具链演进的若干趋势:

从多阶段解耦到紧密流水线: 传统编译将编译、汇编、链接截然分开。以 Mold 链接器以及 Zig/Roc 的尝试为代表,现代工具链正通过共享状态与轻量中间表示,不断打破这些阶段间的重复 I/O 与序列化开销。

热重载理念在系统级开发中的复现: 共享内存直接运行 IR 的思路,让人联想到早年 Smalltalk 或 Lisp 镜像系统的交互式体验,但 Zig 试图在面向硬件控制的静态编译语言中实现相似的高吞吐迭代速度。

安全性策略的不同路径: Rust 侧重于利用编译期借用检查建立静态所有权防线,而 Zig 则倾向于保持语言极简、透明控制,并依赖显式资源传递、安全分配器以及集成模糊测试等运行时与测试工具来捕捉缺陷。

5. 启示与建议

本期讨论为编译器设计与底层系统工程提供了如下参考:

  • 对于编译器与工具链开发者: 审视中间表示(IR)中的引用结构,探索用紧凑的数组偏移索引替代裸指针。这不仅能减少内存占用,还能简化数据结构的磁盘快照与进程间传递。

  • 对于系统架构师: 在设计高吞吐或核心 I/O 服务时,评估显式传递 I/O 接口的可行性。显式参数虽略增函数签名长度,但能大幅降低单元测试中注入故障与模拟网络延迟的复杂度。

  • 对于技术选型团队: 区分工具链中成熟的确定性特性与前沿探索特性。I/O 显式参数化与紧凑数据布局属于行之有效的工程模式;而基于共享内存的运行时通信等特性,适合先在开发辅助或非关键模块中试点评估。

6. 金句摘录

“I didn’t even have to get to type checker. I got it with like the formatter zig format found the bug.” “我甚至还没运行到类型检查器,仅用代码格式化工具 zig format 就发现了那个 Bug。”

—— Kelley 描述将 C 代码移植到 Zig 时的经历,说明编译器对未用常量的严格检查有助于提前拦截隐蔽错误。

“If your anti virus software is interfering with your software development, that is a skill issue.” “如果杀毒软件干扰了你的软件开发,那是使用环境与配置的问题。”

—— 讨论共享内存热加载可能触发安全软件误报时,Kelley 表达对开发者应完全掌控开发机器环境的看法。

“You have a computer your interface to that computer is machine code. We are going to help you write the best possible machine code that you can possibly write.” “你拥有一台计算机,你与它的接口就是机器码。我们的目标是帮助你编写出尽可能优秀的机器码。”

—— Kelley 阐释 Zig 贴近硬件、不隐瞒底层细节的核心设计理念。

“Undefined is bad so I’ll just enum from int zero… No, no, no. That is astronomically worse.” “觉得未定义值不好就直接从零开始枚举……不,那样做反而糟糕得多。”

—— Kelley 评价直接用默认零值掩盖未初始化状态的做法,强调保留显式的未定义状态更有利于在调试阶段暴露内存问题。