Skip to content

【Zig 日报】Zig 创始人 Andrew Kelley 聊 Zig 的 IO 设计和函数颜色问题 #311

@jiacai2050

Description

@jiacai2050

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 的关键节点,双方探讨了如何通过牺牲部分 ergonomics( ergonomics 指开发体验/便利性)来换取对 IO 模型、内存布局及热代码加载的绝对控制。这场对话的结论不仅关乎 Zig 生态的未来走向,更将直接影响那些正在评估是否采用 Zig 构建高性能基础设施的技术决策者——他们必须权衡显式控制带来的测试优势与潜在的工程复杂度。然而,当 Kelley 轻描淡写地 dismiss 杀毒软件对共享内存的拦截为“技能问题”时,一个关于系统级语言在现实企业环境中落地可行性的巨大悬念随之浮现。

2. 核心观点

Andrew Kelley 的核心世界观建立在“显式控制优于隐式安全”的基石之上。他认为,编程语言不应通过隐藏底层细节来提供安全感,而应提供工具让开发者编写尽可能接近机器码的高效代码,同时通过测试而非编译器约束来保证正确性。这一观点在 Rust 主导的“内存安全即默认”浪潮中显得极具争议,因为它要求开发者承担更多手动管理责任,以换取性能上限和架构灵活性。

IO 参数化是解决“函数颜色”问题的工程正解
Kelley 断言,将 IO 作为参数传递(类似 Allocator)而非全局状态,能从根本上抽象同步与异步的差异。其底层逻辑在于,调用者无需关心底层是阻塞还是非阻塞,只需传递不同的 IO 实现即可。这一判断由 Zig 标准库的 "Writergate" 重构背书,尽管引发了社区关于破坏兼容性的争议,但它使得无需维护两套同步/异步标准库成为可能。

基于索引的 IR 设计是极速缓存的前提
嘉宾指出,编译器中间表示(IR)必须摒弃指针而全面采用索引,才能实现高效的持久化。逻辑在于指针随内存地址变化,而索引相对于基地址固定,使得整个模块状态可通过单次 pwritev 系统调用_dump_到磁盘。Roc 编译器的重写计划为此提供了实证,每个模块仅保留约 20 个指针,极大降低了序列化开销。

共享内存是实现无感热加载的关键路径
Kelley 提出了一种激进的架构:编译器与运行时通过命名共享内存通信,解释器直接运行在共享内存的 IR 上。这意味着热代码重载只需切换内存段而无需重新链接。这一判断依赖于 Zig 对内存布局的精确控制能力,旨在解决传统 linker 在增量构建中的性能瓶颈。

编译期错误应优于运行时 Panic
对话强调,编译器应尽可能报告所有错误而非遇到首个 Panic 即停止。Kelley 批评 Rust 文化中泛滥的 unwrap 导致大量本可处理的错误变成了运行时崩溃。Zig 的设计哲学是将内存分配失败等视为可处理错误,确保用户能获得完整的错误报告而非突然死亡。

模糊测试(Fuzzing)应成为标准测试范式
嘉宾认为,依靠人工编写单元测试效率低下,应通过遗传算法驱动模糊测试自动发现边界条件。Zig 计划集成原生 Fuzzing 支持,通过代码覆盖率作为适应度函数演化输入数据。这一判断基于“计算机应自动生成测试”的理念,旨在在用户报告 Bug 前穷尽潜在路径。

这些观点构成了一个严密的逻辑闭环:通过显式参数化获得测试能力,通过索引化 IR 获得序列化能力,最终通过共享内存实现极致的开发体验迭代速度。然而,这一链条的每一个环节都依赖于开发者对底层细节的精确掌控,任何环节的疏忽都可能导致安全性崩塌。

3. 批判与质疑

尽管 Kelley 的论述在技术逻辑上自洽,但从外部视角审视,该体系存在若干未被充分验证的前提。首先,共享内存方案在企业环境中的可行性存疑。Kelley 将杀毒软件拦截共享内存修改行为视为“技能问题”,建议关闭杀毒软件,但这在金融、政府等强合规场景下完全不具操作性。若无法绕过 AV 软件的热度检测,该热加载方案将难以落地。

其次,手动指针修复(Pointer Fixup)引入了新的维护风险。虽然索引化减少了指针数量,但剩余指针的反序列化修复仍依赖人工保证逻辑正确。Kelley 承认这“非常不安全”,一旦遗漏某个指针的修复,程序将在运行时发生难以调试的段错误。这种复杂度是否值得为缓存命中率的提升而承担,仍需大规模生产环境验证。

此外,对话中对于“安全”的定义过于侧重内存安全,而忽略了逻辑安全。Zig 鼓励使用 undefined 作为调试手段,但 Kelley 也提到 AI 助手常误将其替换为 0xaa 导致更隐蔽的错误。这种对未定义行为的依赖,虽然提供了调试便利,但也增加了代码被误用的风险。最后,关于“函数颜色”的解决方案虽然巧妙,但强制所有 IO 函数传递参数是否会导致深层调用链的样板代码爆炸,尤其在非编译器领域的应用中,这一点尚未得到充分讨论。

4. 行业视野

将这场对话置于更广阔的行业图谱中,可以观察到系统编程领域正在发生的范式转移。Zig 与 Roc 的探索印证了“编译器即运行时”的趋势,挑战了传统编译、链接、运行三阶段分离的共识。Kelley 提到的“无需对象文件直接链接”与 Mold 链接器带来的性能革命相呼应,表明行业正在重新审视 linker 的性能瓶颈。

同时,这种设计哲学与历史形成了有趣的历史呼应。共享内存热加载的概念令人联想到 Smalltalk 或 Lisp 机器的镜像系统,但 Zig 试图在系统级语言中复兴这一特性,而非局限于高级语言虚拟机。这与 Rust 追求的“零成本抽象”形成鲜明对比:Rust 试图在编译期通过借用检查器解决安全问题,而 Zig 选择在运行期通过工具链(测试分配器、模糊测试)来捕获错误。

值得注意的是,Kelley 对 unwrap 文化的批评直指 Rust 生态的痛点。随着 Rust 项目规模扩大,运行时 Panic 导致的稳定性问题日益凸显,Zig 的这种“错误即数据”的处理方式可能为行业提供一种替代方案。然而,这也反映了系统编程社区在“安全性”与“控制权”之间的持续张力,这场对话正是这一张力在编译器架构层面的具体投射。

5. 启示与建议

这场对话挑战了“安全必须由编译器静态保证”的假设,强化了“通过架构设计实现可测试性”的价值。对于不同角色的从业者,建议如下:

编译器开发者:应重新评估 IR 设计中的指针使用。尝试将内部引用转换为相对于基地址的索引,这不仅能加速序列化,还能简化多进程间的数据共享。即使不采用 Zig 的共享内存方案,索引化 IR 也能显著降低缓存失效带来的重建成本。

系统架构师:在 design IO 密集型服务时,考虑引入显式的 IO 接口参数而非依赖全局单例。虽然初期会增加函数签名复杂度,但这将使模拟故障注入(如文件不存在、网络超时)变得极其简单,从而大幅提升系统的可测试性和鲁棒性。

技术决策者:需区分强信号与合理推断。Zig 的 IO 设计和索引化 IR 是已验证的强信号,可放心采纳;而基于共享内存的生产环境热加载目前仍属实验性推断,建议在非关键路径或开发环境中先行试点,切勿直接用于高安全要求的生产核心。

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 讲述将一段有 Bug 的 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."
(你拥有一台计算机,你与它的接口是机器码。我们要帮助你写出尽可能最好的机器码。)
语境:阐述 Zig 语言的核心设计哲学,即不隐藏底层细节,而是赋能开发者掌控硬件。

"Undefined is bad so I'll just enum from int zero... No, no, no. That is astronomically worse."
(“未定义值不好,所以我干脆枚举从 0 开始……不不不,那要糟糕天文数字倍。”)
语境:Kelley 吐槽 AI 助手在处理 Zig 代码时,倾向于用看似安全的初始化值替换 undefined,实则破坏了 Zig 利用未定义值检测内存错误的调试机制。

加入我们

Zig 中文社区是一个开放的组织,我们致力于推广 Zig 在中文群体中的使用,有多种方式可以参与进来:

  1. 供稿,分享自己使用 Zig 的心得
  2. 改进 ZigCC 组织下的开源项目
  3. 加入微信群Telegram 群组

Metadata

Metadata

Assignees

No one assigned

    Labels

    日报daily report

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions