跳转至

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 简单得多

核心概念对照表

概念ZigC对比Rust对比
内存管理手动(allocator)手动(malloc/free)所有权系统
错误处理Error Union !返回错误码Result枚举
空值Optional ?TNULL指针Option
泛型comptime参数宏/void*泛型+trait
编译时计算comptime预处理器#defineconst fn(有限)
指针多种指针类型T*&T, &mut T
错误传播try关键字goto/手动?运算符
构建系统build.zig(用Zig写)Makefile/CMakeCargo.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

编译和运行

# 直接运行
zig run src/main.zig

# 构建
zig build
./zig-out/bin/my_project

# 测试
zig build test

快速上手

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?

方面ZigRust
学习曲线中等陡峭
内存安全运行时检查+测试编译时保证
复杂度简单(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 打印调试
  • 内置运行时安全检查(越界等)

参考资源