Zig 语言入门¶
为什么要学¶
Zig 是一门系统编程语言,定位在 C 和 Rust 之间,追求简单和可控:
- 手动内存管理:完全控制内存,无 GC,无隐式分配
- 无隐式行为:所有操作都显式可见,没有隐式类型转换/函数调用
- C 互操作:可以直接导入和调用 C 头文件,零成本 FFI
- 编译时计算:强大的 comptime,在编译期执行任意代码
- 内置交叉编译:一行命令编译到 40+ 平台
- 替代 C 工具链:
zig cc可作为 C/C++ 编译器使用
Bun、TigerBeetle 等知名项目都选择了 Zig。如果你想写高性能系统软件又觉得 Rust 太复杂,Zig 值得一试。
核心概念¶
白话解释¶
Zig 的哲学是"没有魔法": - 看到 + 就是加法,不会偷偷调用 operator overload - 内存分配必须显式传入 allocator,不会偷偷分配 - 错误必须处理,但方式比 Rust 简单得多
核心概念对照表¶
| 概念 | Zig | C对比 | Rust对比 |
|---|---|---|---|
| 内存管理 | 手动(allocator) | 手动(malloc/free) | 所有权系统 |
| 错误处理 | Error Union ! | 返回错误码 | Result枚举 |
| 空值 | Optional ?T | NULL指针 | Option |
| 泛型 | comptime参数 | 宏/void* | 泛型+trait |
| 编译时计算 | comptime | 预处理器#define | const fn(有限) |
| 指针 | 多种指针类型 | T* | &T, &mut T |
| 错误传播 | try关键字 | goto/手动 | ?运算符 |
| 构建系统 | build.zig(用Zig写) | Makefile/CMake | Cargo.toml |
安装配置¶
安装¶
# 方式1: 下载预编译二进制
# https://ziglang.org/download/
# 方式2: 包管理器
# macOS
brew install zig
# Ubuntu(snap)
snap install zig --classic
# 验证
zig version
项目初始化¶
mkdir my_project && cd my_project
zig init
# 生成:
# ├── build.zig
# ├── build.zig.zon
# └── src/
# ├── main.zig
# └── root.zig
编译和运行¶
快速上手¶
Hello World¶
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.print("Hello, {s}!\n", .{"Zig"});
}
变量和类型¶
const std = @import("std");
pub fn main() void {
// 常量
const pi: f64 = 3.14159;
const message: []const u8 = "Hello";
// 变量(必须var)
var count: u32 = 0;
count += 1;
// 类型推断
const x = @as(i32, 42);
// 数组
const arr = [_]u32{ 1, 2, 3, 4, 5 };
// 切片
const slice: []const u32 = arr[1..4];
// 结构体
const Point = struct {
x: f64,
y: f64,
pub fn distance(self: Point, other: Point) f64 {
const dx = self.x - other.x;
const dy = self.y - other.y;
return @sqrt(dx * dx + dy * dy);
}
};
const p1 = Point{ .x = 0, .y = 0 };
const p2 = Point{ .x = 3, .y = 4 };
std.debug.print("距离: {d}\n", .{p1.distance(p2)});
}
错误处理¶
const std = @import("std");
// 定义错误集
const FileError = error{
NotFound,
PermissionDenied,
OutOfMemory,
};
// 返回Error Union: 正常值或错误
fn readConfig(path: []const u8) FileError![]const u8 {
if (std.mem.eql(u8, path, "")) {
return FileError.NotFound;
}
return "config content";
}
pub fn main() !void {
// try: 如果出错则向上传播
const config = try readConfig("app.conf");
std.debug.print("Config: {s}\n", .{config});
// catch: 处理错误
const result = readConfig("") catch |err| {
std.debug.print("Error: {}\n", .{err});
return;
};
_ = result;
// orelse: 提供默认值
const value = readConfig("") catch "default";
std.debug.print("Value: {s}\n", .{value});
}
内存管理(Allocator)¶
const std = @import("std");
pub fn main() !void {
// 通用分配器
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit(); // 退出时检查泄漏
const allocator = gpa.allocator();
// 动态数组
var list = std.ArrayList(u32).init(allocator);
defer list.deinit();
try list.append(1);
try list.append(2);
try list.append(3);
for (list.items) |item| {
std.debug.print("{d} ", .{item});
}
// 动态字符串
var string = std.ArrayList(u8).init(allocator);
defer string.deinit();
try string.appendSlice("Hello ");
try string.appendSlice("Zig!");
std.debug.print("{s}\n", .{string.items});
}
进阶用法¶
1. Comptime(编译时计算)¶
const std = @import("std");
// 编译期执行的函数
fn fibonacci(comptime n: u32) u32 {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// 编译期生成类型
fn Matrix(comptime T: type, comptime rows: usize, comptime cols: usize) type {
return struct {
data: [rows][cols]T,
const Self = @This();
pub fn zeros() Self {
return .{ .data = .{.{0} ** cols} ** rows };
}
pub fn get(self: Self, row: usize, col: usize) T {
return self.data[row][col];
}
};
}
pub fn main() void {
// 编译期就计算好了
const fib10 = comptime fibonacci(10);
std.debug.print("fib(10) = {}\n", .{fib10});
// 编译期生成的类型
const Mat3x3 = Matrix(f32, 3, 3);
const m = Mat3x3.zeros();
std.debug.print("m[0][0] = {}\n", .{m.get(0, 0)});
}
2. C 互操作¶
const c = @cImport({
@cInclude("stdio.h");
@cInclude("stdlib.h");
@cInclude("string.h");
});
pub fn main() void {
// 直接调用C函数
_ = c.printf("Hello from C: %d\n", @as(c_int, 42));
// 使用C的malloc(不推荐,仅示例)
const ptr = c.malloc(100);
defer c.free(ptr);
if (ptr) |p| {
_ = c.memset(p, 0, 100);
}
}
# Zig作为C编译器
zig cc -o hello hello.c
zig c++ -o hello hello.cpp
# 交叉编译C代码
zig cc -target aarch64-linux-gnu hello.c
3. 交叉编译¶
# 编译到不同平台
zig build -Dtarget=x86_64-linux-gnu
zig build -Dtarget=aarch64-linux-gnu
zig build -Dtarget=x86_64-windows-gnu
zig build -Dtarget=aarch64-macos
# 查看支持的target
zig targets | head
4. 异步和并发¶
const std = @import("std");
fn httpGet(allocator: std.mem.Allocator, url: []const u8) ![]u8 {
var client = std.http.Client{ .allocator = allocator };
defer client.deinit();
var response = std.ArrayList(u8).init(allocator);
const result = try client.fetch(.{
.url = url,
.response_storage = .{ .dynamic = &response },
});
if (result.status != .ok) {
return error.HttpError;
}
return response.toOwnedSlice();
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const body = try httpGet(allocator, "http://httpbin.org/get");
defer allocator.free(body);
std.debug.print("Response: {s}\n", .{body[0..@min(200, body.len)]});
}
5. build.zig 构建系统¶
// build.zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "my_app",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// 链接C库
exe.linkLibC();
exe.linkSystemLibrary("sqlite3");
b.installArtifact(exe);
// 运行步骤
const run_cmd = b.addRunArtifact(exe);
const run_step = b.step("run", "Run the application");
run_step.dependOn(&run_cmd.step);
// 测试步骤
const tests = b.addTest(.{
.root_source_file = b.path("src/main.zig"),
});
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&b.addRunArtifact(tests).step);
}
6. 测试¶
const std = @import("std");
const testing = std.testing;
fn add(a: i32, b: i32) i32 {
return a + b;
}
test "基本加法" {
try testing.expectEqual(@as(i32, 4), add(2, 2));
}
test "溢出检查" {
try testing.expectEqual(@as(i32, 0), add(-1, 1));
}
test "内存分配" {
const allocator = testing.allocator; // 测试用分配器(自动检查泄漏)
var list = std.ArrayList(u8).init(allocator);
defer list.deinit();
try list.appendSlice("hello");
try testing.expectEqualStrings("hello", list.items);
}
常见问题¶
Q1: Zig vs Rust?¶
| 方面 | Zig | Rust |
|---|---|---|
| 学习曲线 | 中等 | 陡峭 |
| 内存安全 | 运行时检查+测试 | 编译时保证 |
| 复杂度 | 简单(no macro, no trait) | 复杂(生命周期/trait) |
| C互操作 | 极佳(直接导入.h) | 需要FFI/bindgen |
| 生态 | 小但增长 | 较大 |
| 适合 | 系统编程/替代C | 安全关键系统 |
Q2: Zig 成熟度?¶
Zig 尚未发布 1.0,API 可能变动。适合个人项目和对稳定性要求不高的场景。生产使用需谨慎评估。
Q3: 没有包管理器?¶
Zig 自带包管理(build.zig.zon),但生态还在建设中。查看 https://github.com/zigtools 获取更多工具。
Q4: 调试工具?¶
- 使用 GDB/LLDB 调试
std.debug.print打印调试- 内置运行时安全检查(越界等)
参考资源¶
- Zig 官网 - 官方网站
- Zig 学习 - 在线教程
- Zig GitHub - 源代码
- Zig 标准库文档 - API 参考
- Zig News - 社区新闻