为什么是 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 或异常传播。任何可能失败的操作都必须用 trycatch 显式处理。这一点比 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 在技术谱系中的位置:

特性GoRustZig
内存管理垃圾回收(GC)所有权+借用检查手动管理+分配器模式
运行时有(较大)无(零成本抽象)无(极小)
错误处理多返回值(error)Result 枚举错误联合类型(!T
泛型有(1.18+)有(trait)有(comptime 实现)
异步编程goroutine+channelasync/awaitstd.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 中即可:

bash
1
2
3
4
5
6
7
# 下载并解压
curl -LO https://ziglang.org/download/0.16.0/zig-macos-$(uname -m)-0.16.0.tar.xz
tar xf zig-macos-$(uname -m)-0.16.0.tar.xz
# 移动到系统路径(可选)
sudo mv zig-macos-$(uname -m)-0.16.0 /usr/local/zig
# 加入 PATH
export PATH="$PATH:/usr/local/zig"

验证安装:

bash
1
2
zig version
# 输出: 0.16.0

如果你用 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,适合调试)

这个版本也是各教程中最常见的入门写法,输出到标准错误:

zig
1
2
3
4
5
const std = @import("std");

pub fn main() void {
    std.debug.print("Hello, {s}!\n", .{"World"});
}

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
1
2
3
4
5
const std = @import("std");

pub fn main(init: std.process.Init) !void {
    try std.Io.File.stdout().writeStreamingAll(init.io, "Hello, World!\n");
}

这个签名的设计体现了 Zig 的核心哲学——依赖注入无处不在init.io 提供了当前的 I/O 后端实现,这种模式与 Zig 的分配器(allocator)模式一脉相承——你总是显式地传递依赖,而不是依赖全局状态。std.Io.File.stdout() 获取标准输出的文件句柄,然后通过 writeStreamingAll 写入数据。try 关键字类似于 Rust 的 ? 运算符或 Go 的常见错误检查模式——如果操作失败,错误会向上传播。

两种写法各有用途:简化版适合快速调试、日志输出;标准版适合正式程序,因为它是写 stdout 而非 stderr,且通过 init.io 注入可以支持更灵活的后端(比如测试中替换为内存中的 I/O 实现)。

将上面任意一段代码保存为 hello.zig,然后运行:

bash
1
zig run hello.zig

你应该会看到 Hello, World! 的输出。

总结与下篇预告

在这一篇中,我们从 Go 和 Rust 的视角认识了 Zig 的设计哲学、它在三门语言中的定位,并跑通了第一个程序。总结几个关键结论:

  • Zig 填补了 Go(有 GC、运行时大)和 Rust(学习曲线陡、编译慢)之间的空白
  • 核心设计理念:显式性、零运行时开销、comptime、原生 C 互操作
  • 内存管理通过分配器模式实现,是 Go 开发者最需要适应的地方
  • 编译速度介于 Go 和 Rust 之间,体验良好
  • 0.16 引入了新的 std.process.Init I/O 注入机制

下一篇我们将深入 Zig 的基础语法:变量、常量、基本数据类型和控制流,以及与 Go/Rust 的核心差异点。


系列目录(六篇):

  1. 为什么是 Zig:从 Go/Rust 视角认识一门新系统语言(本篇)
  2. 基础语法:用 Go/Rust 经验快速上手 Zig —— 变量、任意位宽整数、if/switch 表达式、comptime 与 anytype 泛型
  3. 错误处理:Go、Rust 之外的第三条路 —— 错误集、!T、try/catch/errdefer
  4. 内存管理:显式分配器模式 —— 分配器注入、ArenaAllocator、page_allocator
  5. 结构体与 comptime:编译期计算的威力 —— 结构体方法、编译期反射、泛型类型
  6. 标准库、I/O 接口与并发:把知识串起来 —— Unmanaged 容器、std.Io、Io.Group