为什么是 Zig:从 Go/Rust 视角认识一门新系统语言
本文基于 Zig 0.16(2026-04-13 发布,当前最新稳定版)。Zig 是一门仍在快速演进的现代系统编程语言,它的源码仓库已从 GitHub 迁移到 Codeberg,官方下载页在 ziglang.org/download/。
为什么是 Zig?
如果你已经熟悉 Go 和 Rust,可能会问:为什么还要看第三门系统语言?答案很简单:Zig 填补了 Go 和 Rust 之间的空白。
Go 以极低的入门门槛和高开发效率征服了后端与云原生领域,但它的垃圾回收(GC)和较大运行时让它在底层系统编程、嵌入式和实时场景中处处掣肘。Rust 则以零成本抽象和所有权系统提供了极致的性能与安全性,但它的学习曲线陡峭、编译速度偏慢,在快速原型和小型工具场景下显得"重"。
Zig 的定位就是"像 C 一样轻量,像 Rust 一样有现代工具链,像 Go 一样容易上手"——当然,这句话本身有宣传成分,但它确实在三个方向的交叉点上找到了一条独特的路。
Zig 的设计哲学
Zig 常被称为"21 世纪的 C 语言"。它的核心设计理念可以从四个关键词来理解,每个都与 Go 或 Rust 形成对照。
显式性优先。Zig 中没有隐藏的控制流,没有隐藏的内存分配,没有隐式的 panic 或异常传播。任何可能失败的操作都必须用 try 或 catch 显式处理。这一点比 Go 的 if err != nil 更严格——在 Zig 中,函数的返回类型如果是 !T(错误联合类型),调用方必须在某处做出处理,编译器不会默默忽略。相比 Rust 的 ? 运算符,Zig 的 try 是同一思路的另一种实现,但语法更接近过程式风格,Go 开发者适应起来更自然。
无运行时开销。Zig 没有 GC,没有运行时,没有虚拟机。编译产物就是一个原生二进制,可以直接在裸金属或 RTOS 上运行。这使得 Zig 在嵌入式、游戏开发和系统工具领域极具吸引力。对于习惯 Go 运行时(goroutine 调度器、GC 后台线程)的开发者来说,这是最需要调整心智模型的地方——在 Zig 中,你看到的每一行代码都对应真实的机器指令,没有任何隐藏的"管家"。
编译期计算(comptime)。Zig 的 comptime 机制让类型和值都可以在编译期计算,类型是一等公民。你可以把 comptime 理解为"在编译期执行的代码",它替代了泛型、宏、条件编译等各自独立的机制。Rust 开发者会把 comptime 类比为过程宏(proc macro)加泛型的结合体,但使用起来不需要学习两套不同的语法。Go 的泛型(1.18+)通过类型参数实现,而 Zig 的 comptime 更为灵活——你在函数内可以直接对类型做 if 判断、循环甚至调用方法。
与 C 无缝互操作。Zig 可以直接通过 @cImport 导入 C 头文件,不需要编写绑定层或 FFI 胶水代码。这意味着整个 C 生态——成千上万的现有库——都可以直接在 Zig 项目中使用。Go 的 cgo 和 Rust 的 bindgen 各有各的繁琐,而 Zig 把 C 互操作作为语言的一等特性来设计,甚至可以编译 C 代码(Zig 自己捆绑了 clang 作为 C 编译器)。
Go vs Rust vs Zig 核心对比
下面这张表可以帮助已经熟悉 Go 和 Rust 的你,快速定位 Zig 在技术谱系中的位置:
| 特性 | Go | Rust | Zig |
|---|---|---|---|
| 内存管理 | 垃圾回收(GC) | 所有权+借用检查 | 手动管理+分配器模式 |
| 运行时 | 有(较大) | 无(零成本抽象) | 无(极小) |
| 错误处理 | 多返回值(error) | Result 枚举 | 错误联合类型(!T) |
| 泛型 | 有(1.18+) | 有(trait) | 有(comptime 实现) |
| 异步编程 | goroutine+channel | async/await | std.Io 接口+纤程(实验性) |
| 编译速度 | 极快 | 慢 | 快 |
| 学习曲线 | 平缓 | 陡峭 | 中等 |
这张表不是要分高下,而是帮你定位:每个语言都有自己的设计取舍。Zig 选择了"不给开发者增加隐藏负担"的路线——没有 GC 意味着你需要手动管理内存(通过分配器模式),没有运行时意味着你的程序就是二进制本身,错误联合类型让你知道每个函数可能失败的点。
对于从 Go 转过来的开发者,最大的心智转变是内存管理:你需要理解分配器(allocator)的概念,并在代码中显式传递它(这实际上是 Zig 的一个核心模式——依赖注入无处不在,连 I/O 都是通过 init.io 注入的)。对于从 Rust 转过来的开发者,最大的惊喜是编译速度:Zig 的编译速度明显快于 Rust,在迭代开发中体验非常接近 Go。
安装 Zig
Zig 的安装过程非常简洁。前往 ziglang.org/download/ 下载对应你操作系统的压缩包。目前支持 Linux、macOS、Windows 三大平台。
以 macOS 为例,下载 .tar.xz 后解压,将 zig 二进制放到 PATH 中即可:
| |
验证安装:
| |
如果你用 Homebrew,也可以直接 brew install zig,但目前 Homebrew 的版本可能不是 0.16 最新版,建议优先使用官方二进制。
注意:Zig 的源码仓库已从 GitHub 迁移至 Codeberg(codeberg.org/ziglang/zig),后续 issue 和 PR 都在 Codeberg 上管理。
你的第一个 Zig 程序
安装完成后,我们来跑通第一个程序。Zig 0.16 引入了新的 I/O 架构,正式的程序入口签名发生了变化,所以我们会展示两种写法。
简化版:std.debug.print(写 stderr,适合调试)
这个版本也是各教程中最常见的入门写法,输出到标准错误:
| |
std.debug.print 类似于 Rust 的 println! 宏或 Go 的 fmt.Sprintf,但格式字符串的语法更接近 C 的 printf。.{"World"} 是一个匿名元组/匿名结构体字面量,用于向格式化函数传递参数。
标准版:std.Io.File.stdout()(写 stdout,正式入口)
从 Zig 0.16 开始,正式的程序入口签名变成了 pub fn main(init: std.process.Init) !void,I/O 通过 init.io 注入:
| |
这个签名的设计体现了 Zig 的核心哲学——依赖注入无处不在。init.io 提供了当前的 I/O 后端实现,这种模式与 Zig 的分配器(allocator)模式一脉相承——你总是显式地传递依赖,而不是依赖全局状态。std.Io.File.stdout() 获取标准输出的文件句柄,然后通过 writeStreamingAll 写入数据。try 关键字类似于 Rust 的 ? 运算符或 Go 的常见错误检查模式——如果操作失败,错误会向上传播。
两种写法各有用途:简化版适合快速调试、日志输出;标准版适合正式程序,因为它是写 stdout 而非 stderr,且通过 init.io 注入可以支持更灵活的后端(比如测试中替换为内存中的 I/O 实现)。
将上面任意一段代码保存为 hello.zig,然后运行:
| |
你应该会看到 Hello, World! 的输出。
总结与下篇预告
在这一篇中,我们从 Go 和 Rust 的视角认识了 Zig 的设计哲学、它在三门语言中的定位,并跑通了第一个程序。总结几个关键结论:
- Zig 填补了 Go(有 GC、运行时大)和 Rust(学习曲线陡、编译慢)之间的空白
- 核心设计理念:显式性、零运行时开销、comptime、原生 C 互操作
- 内存管理通过分配器模式实现,是 Go 开发者最需要适应的地方
- 编译速度介于 Go 和 Rust 之间,体验良好
- 0.16 引入了新的
std.process.InitI/O 注入机制
下一篇我们将深入 Zig 的基础语法:变量、常量、基本数据类型和控制流,以及与 Go/Rust 的核心差异点。
系列目录(六篇):
- 为什么是 Zig:从 Go/Rust 视角认识一门新系统语言(本篇)
- 基础语法:用 Go/Rust 经验快速上手 Zig —— 变量、任意位宽整数、if/switch 表达式、comptime 与 anytype 泛型
- 错误处理:Go、Rust 之外的第三条路 —— 错误集、
!T、try/catch/errdefer - 内存管理:显式分配器模式 —— 分配器注入、ArenaAllocator、page_allocator
- 结构体与 comptime:编译期计算的威力 —— 结构体方法、编译期反射、泛型类型
- 标准库、I/O 接口与并发:把知识串起来 —— Unmanaged 容器、std.Io、Io.Group