Zig 入门:还没有 1.0 的系统语言,为什么值得看

这篇要解决的问题

系统编程这个位置长期被 C 和 C++ 占着,近年 Rust 的方案是”用强类型和借用检查把内存错误挡在编译期”,代价是学习曲线陡。Zig 走的是另一条路:不引入隐藏的控制流(没有异常、没有隐藏的构造/析构、没有运算符重载),内存安全靠显式约定加一批编译期检查,语法上刻意保持接近 C 的直白。它的口号是”一门更好的 C”:既能写操作系统、数据库、编解码器,也能像 C 一样直接编译 C 代码(zig cc 可当作跨平台 C 编译器用)。生态里最出圈的证据是 Bun——那个 Node.js 的竞争运行时就是用 Zig 写的。这篇按”装起来、看懂错误处理和可空类型、见识 comptime、评估 pre-1.0 风险”的顺序走,写作时的最新稳定版是 0.14.1(2025 年 5 月发布),1.0 没有时间表。

安装与 zig run

Zig 是单个可执行文件加标准库,没有中央包服务依赖。官网下载对应平台的压缩包解压即可,或用包管理器(macOS 的 brew、Windows 的 scoop/winget 都有维护);Linux 发行版 apt 源里的版本通常严重滞后,不建议。写第一个程序:

# hello.zig 放哪都行,zig run 直接解释编译执行
zig run hello.zig

1. 要做正经项目时,zig init 生成 build.zig 骨架
zig init
zig build          # 编译产物在 zig-out 下
zig build test     # 跑测试

下面这段 hello 演示了 Zig 的两个招牌习惯:defer 和显式分配器。标准库里凡是要内存的函数,都要求你把分配器当参数传进去——分配策略是调用方的决定,库不偷偷 malloc:

const std = @import("std");

pub fn main() !void {  // !void:可能正常返回,也可能返回错误
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();  // defer:作用域结束必然执行,清理代码挨着申请代码
    const allocator = gpa.allocator();

    const buf = try allocator.alloc(u8, 32); // 要内存必须显式申请
    defer allocator.free(buf);
    @memcpy(buf[0..5], "hello");

    const stdout = std.io.getStdOut().writer();
    try stdout.print("{s} from Zig!n", .{buf[0..5]});
}

显式错误处理与可选类型

Zig 的错误处理和 Go 解决同一个问题,机制却不同。Go 的 error 是接口值,任何返回值都可能裹着错误;Zig 把”可能怎么失败”写成错误集(error set)放进类型签名,函数返回类型 ConfigError!u32 读作”要么是 u32,要么是那两个错误之一”:

const ConfigError = error{ NotFound, TooSmall };

fn readLimit(raw: []const u8) ConfigError!u32 {
    const n = std.fmt.parseInt(u32, raw, 10) catch return error.NotFound;
    if (n < 1024) return error.TooSmall;
    return n;
}

pub fn main() !void {
    // try 是 Go 里 "if err != nil { return err }" 的浓缩写法:出错就继续上抛
    const limit = try readLimit("80");

    // catch 是就地兜底:出错给默认值,不让错误往上传
    const port = readLimit("abc") catch 8080;

    std.debug.print("limit={d} port={d}n", .{ limit, port });
}

可空值同理。Zig 没有 null 随处可点的自由,?T 类型想用里面的值,编译器强制你先拆包:

fn findUser(id: u32) ?[]const u8 {  // ?T:要么有 T,要么 null
    return if (id == 1) "alice" else null;
}

pub fn main() void {
    const name = findUser(2) orelse "anonymous"; // orelse 拆包给兜底
    std.debug.print("{s}n", .{name});
}
维度 Go Zig
错误表示 error 接口值,与返回值同居 错误集写进返回类型,值与错误互斥
上抛 if err != nil 手写返回 try 一个关键字
就地兜底 if err != nil 里赋默认值 catch 表达式
可空值 nil 指针/ok 惯用法 ?T 类型,编译器强制拆包
资源清理 defer defer / errdefer(仅出错路径执行)
隐藏控制流 panic 可从深处冒出 没有异常,错误只沿返回值走

comptime:编译期把泛型问题解决掉

Zig 没有单独的泛型语法、宏系统或模板引擎,取而代之的是 comptime:任何标记为编译期执行的代码就在编译期跑。最常见用途是拿”类型”当参数,为每个类型实例化出专门的代码:

const std = @import("std");

// T 和 N 是编译期参数:Stack(i32, 4) 会在编译期生成一个全新类型
fn Stack(comptime T: type, comptime N: usize) type {
    return struct {
        items: [N]T = undefined,
        len: usize = 0,

        pub fn push(self: *@This(), v: T) !void {
            if (self.len == N) return error.Overflow; // 满了怎么办由调用方决定,不静默扩容
            self.items[self.len] = v;
            self.len += 1;
        }
    };
}

pub fn main() !void {
    var s: Stack(i32, 4) = .{};
    try s.push(42);
    std.debug.print("top={d} cap={d}n", .{ s.items[0], s.items.len });
}

这套机制取代了 C++ 模板的多数用途,且报错发生在编译期你自己的代码里,而不是模板展开三千行的深渊里。代价是没有”运行时多态”,需要接口的地方得用函数指针或 tagged union 自己组合。

pre-1.0 的诚实风险

三条风险写在前面。第一,语言本身还在动:Zig 的版本升级经常带走语法和标准库 API,三年前的教程代码今天大概率编译不过,查资料必须以当前版本文档为准。第二,并发是半成品:async/await 机制长期处于搁置状态,何时以何种形式回归没有时间表,现阶段并发主要靠 std.Thread 和系统原语,写惯 Go channel 的要重新掂量。第三,生态与岗位都小:没有大公司背书(项目由 Zig 软件基金会靠赞助维持),第三方库数量和招聘需求都不能与 Go/Rust 比。

反模式点名:拿 pre-1.0 的语言承载关键生产路径,然后被一次版本升级打崩。现阶段 Zig 合适的位置是个人工具、性能敏感的小组件、替换手写 C 的场景、以及”跟着 Bun/TigerBeetle 这类标杆项目学系统知识”;公司核心业务请留在成熟语言里观望。

延伸阅读:Go 的错误处理对照着看更有感觉,见《Go 错误处理》;两门语言并发的取舍差异见《Go 并发基础》;用 Zig 交叉编译 C 代码时用到的平台常识见《Linux 常用命令》。

版本核实(以官方文档为准):写作时最新稳定版为 0.14.1(2025-05-23 发布,0.14.0 于 2025-03-05 发布),1.0 未发布;async 处于搁置状态是官方公开表态;zig run/init/build、error set、try/catch、?T、comptime、显式分配器均为 0.14 已发布行为,具体 API 以 ziglang.org 当前版本文档为准。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇