Zig 为什么值得看
Zig 是一个定位为“更好的 C”的系统级语言:没有隐藏控制流、没有隐藏内存分配、没有宏、没有异常,同时提供了 C 无法想象的元编程能力——comptime。
它的思路是:既然编译器在编译时已经知道所有类型信息,那为什么不干脆让你在编译期执行普通 Zig 代码?
comptime 基础:编译期执行的普通代码
const std = @import("std");
// 编译期计算斐波那契,结果直接内联为常量
fn fib(comptime n: u64) u64 {
if (n < 2) return n;
return fib(n - 1) + fib(n - 2);
}
pub fn main() void {
// fib(20) 在编译期求值,运行时零成本
std.debug.print("fib(20) = {}\n", .{fib(20)});
}与普通常量表达式不同,Zig 的 comptime 代码就是普通函数体——可以有循环、分支、递归。
泛型:类型只是 comptime 的值
Zig 没有 <T> 语法。所谓泛型,就是“接收 type 作为参数的函数返回类型”:
fn Stack(comptime T: type) type {
return struct {
const Self = @This();
items: []T = &.{},
allocator: std.mem.Allocator,
pub fn init(allocator: std.mem.Allocator) Self {
return .{ .allocator = allocator };
}
pub fn push(self: *Self, v: T) !void {
self.items = try self.allocator.realloc(self.items, self.items.len + 1);
self.items[self.items.len - 1] = v;
}
pub fn pop(self: *Self) ?T {
if (self.items.len == 0) return null;
const v = self.items[self.items.len - 1];
self.items = self.allocator.resize(self.items, self.items.len - 1) orelse self.items[0..self.items.len - 1];
return v;
}
};
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var s = Stack(i32).init(gpa.allocator());
try s.push(42);
std.debug.print("{}\n", .{s.pop().?});
}Stack(i32) 是一次类型构造调用,Stack(f64) 会生成另一份完全独立、无装箱的代码——零成本抽象。
编译期反射:生成序列化代码
fn FieldNames(comptime T: type) []const []const u8 {
const info = @typeInfo(T);
if (info != .@"struct") @compileError("需要结构体类型");
const fields = info.@"struct".fields;
var names: [fields.len][]const u8 = undefined;
inline for (fields, 0..) |f, i| {
names[i] = f.name;
}
return &names;
}
const User = struct { id: u32, name: []const u8, active: bool };
pub fn main() void {
for (FieldNames(User)) |n| {
std.debug.print("{s} ", .{n});
}
// 输出:id name active
}inline for 会在编译期展开循环,@typeInfo 提供完整类型反射。基于这些,可以在编译期生成 JSON 序列化、ORM 映射、CLI 参数解析,而运行时没有任何反射开销。
编译期断言与错误
const Config = struct {
port: u16,
comptime {
// 编译期校验:不符合直接报错,无法进入运行时
if (@sizeOf(Config) > 64) {
@compileError("Config 结构体过大");
}
}
};
// 编译期测试
comptime {
if (fib(10) != 55) @compileError("fib 实现有误");
}这是 Zig 取代“断言 + 单元测试”的一种方式:能被静态验证的东西,就不该等到运行时。
与 C 的互操作
// 直接 include C 头文件,无需绑定层
const c = @cImport({
@cInclude("stdio.h");
@cInclude("sqlite3.h");
});
pub fn main() void {
_ = c.printf("hello from C\n");
}# Zig 甚至自带 C/C++ 编译器
zig cc -O2 -o app main.c
zig build-exe main.zig -lczig cc 是一个开箱即用的交叉编译工具链,一条命令就能产出任意目标平台的可执行文件,这也是 Zig 在基础设施领域迅速流行的原因之一。
需要注意的现实
- 标准库与生态仍在演进,0.x 版本间有破坏性变更,生产项目需锁定版本。
- 手动内存管理是常态:所有分配都要显式传入 allocator,写起来更啰嗦,但内存行为完全可预测。
- 没有隐藏分配这一约束让异步、IO 的库设计仍不稳定,选型时需评估。
小结
Zig 的 comptime 把“元编程”从一门独立语言(模板、宏)简化成了“在编译期跑普通代码”。如果你写 C/C++、做嵌入式或高性能基础设施,Zig 值得投入一个周末认真体验。
评论 0
还没有评论,来说两句吧。