为什么已经有 C++、D 和 Rust 了,还要用 Zig?
Zig 的设计哲学——回避隐藏控制流、隐式分配、运算符重载和 RAII 风格析构函数——让它与 C++、D、Rust、Go 乃至 Jai 形成了鲜明对比。支持者认为,显式错误处理、显式传递分配器,以及面向裸机的标准库,使 Zig 非常适合低层系统、嵌入式开发,以及作为现代 C 替代品,即使这会牺牲一些易用性和抽象能力。怀疑者则认为,这些特性使它不太适合高层应用或科学计算代码,并对语言稳定性、生态成熟度以及缺少 traits、更丰富的错误载荷和更适合数学的运算符重载等问题表示担忧。
文章更新与范围
- Zig 的原始对比页面已更新,修正了一些过时观点,尤其是“不提供包管理器”这一点。
- 一些评论者觉得页面只是罗列特性,却没有清楚说明“如果你在做 X,就该用 Zig”,因此在科学计算等特定领域里更难判断是否适合。
隐藏控制流与控制流定义
- 大型子线程在争论“控制流”到底是什么意思:有人把它严格限定为条件分支;也有人把任何执行顺序的变化都算进去(函数调用、异常、中断、goto)。
- Zig 的“没有隐藏控制流”通常被解释为“没有看不见的函数调用或基于异常的跳转”:你应该在调用点就能看到所有分支和错误传播。
- 批评者认为
defer/try本身就是不那么显然的控制流关键字,而且“hidden(隐藏)”这个词本身也有多重含义。
内存分配与分配器
- 显式传递分配器被辩护为一种控制反转,并且可以避免“函数颜色”问题。
- 有人认为额外参数只是轻微开销;全局分配器仍然是可选项。
- 担忧点在于:库仍可能自己创建分配器或直接调用操作系统。支持者表示,这在 Zig 里会被视为不符合惯用写法。
- 讨论中还提到其他语言里的基于能力的设计;参与者争论,对于像 Zig 这样“没有运行时”的系统语言来说,这种约束很难或不可能强制实现。
错误处理与异常
- Zig 的
try/错误联合类型强制在调用点处理或显式传播,与 C++/Java 中隐藏的异常以及 Go/Rust 中的 panic 形成对比。 - 有人觉得行内错误处理会很啰嗦;也有人认为 Zig 的
try轻量且比受检异常更清晰。 - 讨论还涉及 Go 的 panic/recover 算不算异常;一些人坚持它们就是“穷人版异常”。
RAII、析构函数与资源清理
- 缺少 RAII/析构函数对几位参与者来说是决定性的劝退点,他们认为显式清理调用既冗长又容易出错。
- 另一些人更偏好显式生命周期、
defer和分配器模式,认为这会让清理点更清楚,并鼓励不依赖析构函数的设计。 - Rust 的 RAII 模型被提及为一种很有吸引力的中间路线。
运算符重载、抽象,以及数学/字符串
- 不支持运算符重载被赞为可以避免“隐藏调用”和过度聪明的 DSL。
- 反对者说这会损害数值计算和线性代数代码,也会让字符串拼接和自定义数值类型更笨拙;其他语言中的
+/~风格语法被认为更顺手。 - 支持 Zig 的一方认为抽象应该是显式函数;批评者回应说,在可表达性面前,“少踩坑”有时被高估了。
使用场景与目标受众
- 支持者强调 Zig 的简洁、可选标准库以及面向裸机的设计,非常适合内核、嵌入式、EFI 和低层系统开发。
- 有人认为它在游戏开发和工具链领域也有前景(例如终端模拟器、Web 运行时);但另一些人怀疑它是否适合高层、数学密集或企业风格的代码库。
稳定性、生态与采用风险
- 讨论了语言稳定性的时间表,以及在 1.0 之前押注 Zig 是否明智。
- 一方强调早期采用者会承担迁移成本,并将其与 Rust 在 1.0 之前的频繁变动以及 Python 2→3 做类比;另一方则指出,现实中的使用正在增长(例如 Web 工具、交易系统),说明 Zig 不只是玩具。
- 人们在“想要 C 互操作”与“想要安全保证”之间存在张力:大量复用 C 会削弱 Zig 的部分安全叙事。
与 D、Rust、Go、Jai、C 等的比较
- D 用户反馈性能不错,也有多种分配策略,仅凭这页并不足以说服他们切换。
- Rust 因安全性受到赞扬,但也因复杂度、工具问题以及缺少分配器 API / 可移植 SIMD 而受批评;一些 Rust 用户也对 Zig 感兴趣。
- 有人指出 Go 没有 try/catch;它的 panic 很少用于控制流,但确实会隐藏一些分配(例如
defer)。 - Jai 也被提到,但目前因闭源、未发布以及缺少 32 位嵌入式支持而被搁置。
- 还有人认为 C 或类似 C 的后继者(如 C3 等)依然更简单;另一些人则反驳说,C 的“简单”掩盖了“会写 C”和“会写安全、正确的 C”之间的巨大鸿沟。