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, gemini-2.5-pro)

1. 导读

在这期访谈中,Zig 创造者 Andrew Kelley 与 Roc 语言设计者 Richard Feldman 就系统编程语言的设计权衡展开了讨论。Roc 正在使用 Zig 重写编译器,两人围绕实际工程中的底层设计抉择展开了交流。

对话重点讨论了 Zig 的显式 IO 参数传递机制、基于索引替代指针的紧凑内存布局、并发(Concurrency)与异步(Asynchrony)的语义界定,以及编译器将未使用的变量提升为致命错误的考量。

2. 核心观点

Andrew Kelley 认为:系统编程语言应当提供对底层机器行为与内存布局的清晰控制,通过显式表达副作用与精确管理资源来保障代码的可预测性与执行效率。 相比引入复杂的隐式抽象层,他更倾向于将调度与内存职责交由开发者显式管理,并辅以确定性的测试与故障注入工具。

判断一:显式IO参数传递无关“函数颜色”,关乎架构的可测试性与灵活性

  • 断言:Zig 的 IO 设计倾向于让执行 IO 操作的函数显式接收一个 IO 句柄或参数。这并非纯粹的“函数颜色”分化,而是一项旨在提升解耦与可测试性的架构决策。
  • 逻辑:Kelley 认为,关键在于避免维护两套平行的同步与异步标准库。通过将 IO 能力显式作为参数传入,业务逻辑与具体的底层实现(同步阻塞、事件驱动或测试模拟环境)实现解耦。在测试环境中,可以轻松传入 Mock 实现,主动注入文件缺失、网络超时等边界故障,以验证系统的容错表现。
  • 佐证:Zed 编辑器在 Rust 实现中也采用了显式传递 FS(文件系统)上下文以支持测试隔离的模式。Zig 计划借助类似机制,对 IO 错误与内存分配失败进行详尽的测试覆盖。

判断二:极致性能源于对内存布局的绝对控制,而非仅仅算法优化

  • 断言:在编译器等高吞吐系统中,采用“索引替代指针”(Index-based referencing)的数据结构能够显著提升内存局部性,并简化序列化与跨进程共享。
  • 逻辑:当抽象语法树(AST)等复杂节点使用相对索引而非绝对指针时,整个数据结构可以被组织在少数连续的内存池(Arena)中。写出到磁盘可以直接通过 pwritev 系统调用将连续内存块一次性持久化,开销极小。重新加载时只需整体映射到内存,通过极少数顶层偏移修正即可完成重建。
  • 佐证:Roc 编译器的重构便遵循这一思路,各模块大幅减少内部裸指针引用,主要依赖紧凑数组加索引定位,并借此支持直接基于共享内存的热重载更新机制。

判断三:并发(Concurrency)与异步(Asynchrony)是两种不同且必须在代码中明确区分的需求

  • 断言:引用 Loris Cro 的观点,Kelley 强调了并发与异步在语义上的根本差异:异步表示操作可以乱序执行;而并发则表示多个任务必须非阻塞地交替推进,否则可能引发死锁。
  • 逻辑:写入两个独立文件属于异步行为——无论顺序执行还是交错执行均不影响最终逻辑。但在单线程测试中同时模拟服务端 accept 与客户端 connect 则属于并发需求:若服务端操作完全阻塞,客户端逻辑将无法获得调度,从而陷入死锁。因此,系统库和语言语法需要提供能够明确表达“必须交替运行”的原语。
  • 佐证:该场景常见于网络协议的单元自测,若无法在调度层区分二者,同一套代码在不同的事件循环实现下可能会表现出不同的死锁行为。

判断四:编译器警告是待修复的Bug,应当默认阻断流程

  • 断言:将“未使用的变量”等传统警告升级为硬性编译错误(Error),能够促使开发者及时清理潜在的逻辑歧义。
  • 逻辑:在工程实践中,编译警告往往容易被忽略甚至被增量构建系统缓存所掩盖。未使用的变量往往是重构遗漏、无效分支或拼写错误的诱因。在编译期直接报错,能够促使开发者在编码阶段就保持逻辑整洁。
  • 佐证:Kelley 分享过将一段 C 代码迁移至 Zig 的经历:正是因为 Zig 强制检查未使用的变量,促使他将变量改写为常量,从而立即发现了原 C 代码中隐藏的死代码分支。

四项判断共同构成了 Zig 的工程哲学:显式呈现 IO 副作用、精细组织内存排布、严格区分并发语义,并通过严苛的编译期检查换取生产环境的确定性。

3. 批判与质疑

这种强调显式控制与低级抽象的哲学,同样存在实际工程取舍:

首先,对开发团队的整体工程素养与纪律性(discipline)要求极高。 显式传递 IO 句柄和手动规划基于索引的数据结构,在大型团队协作或人员水平参差不齐时,可能带来更高的心智负担和初期上手成本。

其次,基于共享内存的热重载机制复杂度较高。 跨进程直接共享内存并在目标进程中动态修补指针基址,在工程实现上面临极高的细致度要求。一旦出现指针偏移或内存对齐偏差,排查崩溃(如段错误)的难度显著增加。

第三,系统级操作与现代终端安全策略的兼容性摩擦。 在运行进程中动态注入或修改内存的技术,在受严格合规管制的企业开发环境或开启终端防护软件的主机上,容易触发安全软件的拦截与误报。

第四,依靠手动控制与严格测试保障安全的长期普适性。 相比编译器级所有权与借用检查(如 Rust 提供的类型系统级别静态证明),Zig 的可靠性更多依赖严密的单元测试、调试分配器与工程规范,在更大规模的开源社区协作中其防护上限仍需长期观察。

4. 行业视野

这场讨论反映了现代系统编程语言演进中的重要趋势:

重返硬件共鸣(Mechanical Sympathy)。 在经历多层抽象堆叠后,开发者重新关注 CPU 缓存行对齐、数据局部性与低系统调用开销对系统吞吐的决定性作用。

系统安全范式的多元化探索。 面对内存安全挑战,除依赖编译器复杂类型系统进行静态证明外,通过极简语法、消除未定义行为、显式错误处理及完善测试分配器来保障正确性,构成了另一种工程实践路径。

交互式编译与极速反馈的追求。 通过紧凑内存排布与近乎零开销的序列化,探索面向系统编程语言的亚秒级热重载与增量编译,尝试在编译型语言中实现动态环境的开发体验。

5. 启示与建议

这场对话提示开发者重新权衡抽象层级与可维护性的关系:

针对系统级软件与编译器开发者:

  1. 尝试索引与连续内存池设计: 在对内存开销敏感的数据结构中,探索采用数组加索引替代深层嵌套指针,以增强缓存局部性并提升序列化效率。
  2. 以可测试性驱动 IO 接口设计: 避免在核心逻辑中硬编码全局系统调用,通过显式注入环境上下文使边界故障测试变得可复现。

针对语言设计与基础架构团队:

  1. 明确区分并发与异步的原语语义: 在异步运行时设计中,为乱序并行与必须交错的协同任务提供清晰的 API 表达方式。
  2. 审慎评估警告与错误的边界: 结合工程团队实际痛点,在开发期体验(快速原型)与构建期严谨性(严格检查)之间寻找合理的平衡。

结论强弱判断: 紧凑内存排布对缓存与序列化性能提升的论断属于经过工业界广泛验证的强信号;而基于共享内存的热重载方案与特定语言设计理念的普及度,则属于尚在演进中的合理推断。

6. 金句摘录

  1. “it’s an engineering question like do you need a copy do you need an async copy of the standard library or not that’s the question for me”

    • 中文意译: “这对我而言是个工程问题:你是否真的需要一套完全平行的异步标准库?这才是核心所在。”
    • 语境: 回应关于显式 IO 参数是否属于“函数颜色”分化的争论,说明其初衷是通过参数化统一同步与异步接口,避免代码库重复建设。
  2. “concurrency is not parallelism… he introduces a third word which is asynchrony”

    • 中文意译: “并发不等于并行……(Loris Cro)引入了第三个词:异步。”
    • 语境: 区分系统编程中常被混淆的术语,指出异步强调“允许乱序”,而并发强调“多任务必须交替推进以避免死锁”。
  3. “it’s kind of like memory allocation, isn’t it? Like it’s just function memory allocation. It’s the same problem as a memory allocator”

    • 中文意译: “这其实很像内存分配,不是吗?它本质上就是针对函数的内存分配,解决的是与内存分配器相同的空间管理与碎片问题。”
    • 语境: 在讨论增量链接器为修改后的函数分配存储空间时,指出其与通用内存分配在底层算法逻辑上的共通性。
  4. “If you catch a virus with anti virus software, it is too late. Like that’s the wrong solution to the job.”

    • 中文意译: “如果你依赖杀毒软件去拦截病毒,那往往已经太迟了。这不是从根本上解决问题的合理方式。”
    • 语境: 讨论运行时开发工具可能被防护软件误报的问题,借此表达对安全理念的看法:强调源头预防与严谨设计,而非单纯依赖事后被动拦截。