1605 字
8 分钟
Zig 语言深度剖析:它凭什么挑战 C 语言的王座?

引言:后 C 时代的权力真空#

在过去的 50 年里,C 语言一直统治着系统编程的底层。操作系统、驱动程序、嵌入式系统,无一不是 C 的天下。 Rust 的出现,试图用“内存安全”和“所有权机制”来革 C 的命。但 Rust 的陡峭学习曲线(Lifetime, Borrow Checker)让很多 C 程序员望而却步。

这时候,Zig 站了出来。 Zig 的口号不是“比 C 更安全”,而是“比 C 更 C”。 它没有复杂的隐式行为,没有预处理器,没有宏,但它有强大的编译时计算 (comptime) 和极其现代的错误处理机制。

Bun(那个比 Node.js 快 3 倍的运行时)是用 Zig 写的。 TigerBeetle(那个每秒处理百万级金融交易的数据库)是用 Zig 写的。 Uber 正在用 Zig 重写部分基础设施。

Zig 到底有什么魔力?本文将带你从源码层面一探究竟。


一、Zig 的核心哲学:没有隐藏控制流#

在 C++ 或 Rust 中,当你看到 a + b 时,你不知道发生了什么:

  • 它可能调用了一个运算符重载函数。
  • 它可能抛出了一个异常。
  • 它可能触发了一个构造函数。

在 Zig 中,a + b 就是加法。如果可能会溢出,你必须显式写 a +% b (Wrapping Add)。 Zig 承诺:代码做了什么,看一眼就得知道。 没有宏魔法,没有构造函数,没有析构函数。

1.1 Hello World 的启示#

const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.print("Hello, {s}!\n", .{"World"});
}

注意那个 !void。这代表 main 函数可能会返回一个错误。 Zig 将 错误处理 视为一等公民 (First-class Citizen)。


二、Comptime:编译时计算的终极形态#

C++ 有 templateconstexpr,Rust 有 macro。 Zig 说:为什么不直接用 Zig 语言自己来做编译时计算呢?

在 Zig 中,只要变量被标记为 comptime,它就会在编译阶段被求值。这意味着你可以写出极其强大的泛型代码,而不需要学习一套新的元编程语法。

2.1 实战:在编译时解析 JSON#

想象一下,你可以在编译代码的时候,读取一个 JSON 配置文件,并根据配置生成相应的 Struct 定义。如果 JSON 格式不对,编译就会失败

fn Matrix(comptime T: type, comptime width: usize, comptime height: usize) type {
return struct {
data: [width * height]T,
fn at(self: @This(), x: usize, y: usize) T {
return self.data[y * width + x];
}
};
}
pub fn main() void {
// 这个类型是在编译时动态生成的!
const Mat4x4 = Matrix(f32, 4, 4);
var m = Mat4x4{ .data = undefined };
// ...
}

这段代码展示了 Zig 如何用普通函数来实现泛型。没有 <T>,没有 trait,只有函数和类型。


三、内存管理:手动,但从容#

Zig 没有垃圾回收 (GC),也没有 Rust 那样的所有权 (Ownership) 检查。 它回归了手动管理内存,但给了一把手术刀:Allocator

3.1 显式的 Allocator#

在 C 语言中,malloc 是一个全局函数。 在 Zig 中,没有任何函数会通过“魔法”申请内存。如果一个函数需要分配内存,它必须接受一个 Allocator 参数。

fn concat(allocator: std.mem.Allocator, a: []const u8, b: []const u8) ![]u8 {
const result = try allocator.alloc(u8, a.len + b.len);
@memcpy(result[0..a.len], a);
@memcpy(result[a.len..], b);
return result;
}

这种设计让你一眼就能看出:

  1. 这个函数会分配内存。
  2. 内存可能会分配失败(返回 ! 错误)。
  3. 你需要负责释放这个 result

3.2 Defer 与 Errdefer#

为了防止忘记释放内存,Zig 引入了 defer

const memory = try allocator.alloc(u8, 100);
defer allocator.free(memory); // 函数退出时执行
// ... 复杂的逻辑 ...
if (something_wrong) return error.Oops;

还有一个更酷的:errdefer。只有当函数返回错误时才执行。这在初始化复杂结构体时非常有用。

const self = try allocator.create(MyStruct);
errdefer allocator.destroy(self); // 如果后续步骤失败,撤销这一步
self.buffer = try allocator.alloc(u8, 1024);
// 如果这行失败了,errdefer 会触发,释放 self。
// 如果这行成功了,函数正常返回,errdefer 不触发,self 此时归调用者管理。

四、Zig Build System:抛弃 Make/CMake#

C/C++ 项目的构建一直是个噩梦。Makefile, CMake, Ninja, Autotools… Zig 自带了一个构建系统,而且构建脚本是用 Zig 语言 写的 (build.zig)。

这意味着你可以用你熟悉的语言逻辑来编排构建流程,不仅可以编译 C/C++ 代码(是的,Zig 编译器可以直接编译 C 代码,甚至比 GCC/Clang 更好用),还可以管理依赖。

build.zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const exe = b.addExecutable(.{
.name = "demo",
.root_source_file = .{ .path = "src/main.zig" },
.target = b.standardTargetOptions({}),
.optimize = b.standardOptimizeOption({}),
});
// 直接集成 C 代码
exe.addCSourceFile(.{ .file = .{ .path = "src/legacy.c" }, .flags = &.{} });
exe.linkLibC();
b.installArtifact(exe);
}

zig cc 命令甚至可以直接替代 gcc。很多 Rust 项目现在都用 zig cc 作为链接器来实现交叉编译,因为它自带了所有平台的 libc,不需要你去折腾环境。


五、Zig vs Rust:既生瑜,何生亮?#

特性RustZig
内存安全编译时强制检查 (Borrow Checker)运行时检查 (Debug模式) + 手动管理
学习曲线极高 (就像学数学)低 (就像学更好的 C)
隐式行为有 (Drop trait, Deref coercion)无 (No hidden control flow)
元编程宏 (Macro)Comptime
交叉编译较麻烦开箱即用 (God tier)

我的观点

  • 如果你在写一个大型、多人协作、业务逻辑复杂的系统(如浏览器内核、大型后端服务),Rust 的强制安全性是必须的。它能防止队友写出愚蠢的 Bug。
  • 如果你在写底层基础设施、高性能中间件、游戏引擎、嵌入式开发,或者你是一个控制欲极强的程序员,Zig 会让你感到无比顺手。它像一把精密的瑞士军刀,没有任何多余的装饰。

六、结语#

Zig 目前还在 v0.11/0.12 阶段,尚未到达 v1.0 稳定版。 这意味着它的语法还在变动。 但它的潜力已经无法忽视。 它不想成为下一个“更好的 Java”或“更好的 Python”。 它想成为下一个 C。一个 50 年后依然有人在用的语言。

作为开发者,我们有幸见证这种语言的诞生。 如果你厌倦了 C++ 的臃肿和 Rust 的说教,不妨试试 Zig。 也许,这就是你一直在寻找的那把剑。

Zig 语言深度剖析:它凭什么挑战 C 语言的王座?
https://www.oferry.com/posts/a67/
作者
晨平安
发布于
2026-02-10
许可协议
CC BY-NC-SA 4.0
封面
示例歌曲
示例艺术家
封面
示例歌曲
示例艺术家
0:00 / 0:00