Zig 是一门现代化的系统级编程语言,由 Andrew Kelley 创建。它的设计理念强调显式优于隐式编译时代码执行零成本抽象以及与 C 语言的完美互操作性。本文将全面介绍 Zig 的基础语法。

变量与常量

变量声明

Zig 使用 var 声明可变变量,使用 const 声明常量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const std = @import("std");

pub fn main() void {
// 可变变量
var x: i32 = 10;
x = 20; // 可以修改

// 常量
const y: i32 = 30;
// y = 40; // 错误:不能修改常量

// 类型推断
var z = 50; // 自动推断为 comptime_int
}

基本数据类型

Zig 提供了丰富的整数和浮点类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 有符号整数
const a: i8 = -128; // 8位
const b: i16 = 1000; // 16位
const c: i32 = 100000; // 32位
const d: i64 = 1000000; // 64位

// 无符号整数
const e: u8 = 255;
const f: u32 = 4000000000;

// 浮点数
const g: f32 = 3.14;
const h: f64 = 3.14159265359;

// 布尔类型
const flag: bool = true;

// 字符类型(UTF-8 码点)
const ch: u21 = '中';

未定义值

Zig 要求显式处理未初始化的变量:

1
2
var x: i32 = undefined;  // 显式标记为未定义
x = 10; // 使用前必须初始化

数组与切片

数组

数组是固定大小的同类型元素集合:

1
2
3
4
5
6
7
8
9
10
11
// 固定大小数组
const arr = [5]i32{ 1, 2, 3, 4, 5 };

// 使用 _ 让编译器推断大小
const arr2 = [_]i32{ 1, 2, 3 };

// 重复初始化
const zeros = [_]i32{0} ** 10; // 10个0

// 获取数组长度
const len = arr.len; // 5

切片

切片是对数组的引用,包含指针和长度:

1
2
3
4
5
6
7
8
9
10
var arr = [_]i32{ 1, 2, 3, 4, 5 };

// 创建切片
const slice = arr[1..4]; // 元素 2, 3, 4

// 切片可以修改原数组
slice[0] = 100; // arr 变为 { 1, 100, 3, 4, 5 }

// 字符串是 u8 切片的常量
const str: []const u8 = "Hello, Zig!";

多维数组

1
2
3
4
5
6
// 2D 数组
const matrix = [3][3]i32{
[_]i32{ 1, 2, 3 },
[_]i32{ 4, 5, 6 },
[_]i32{ 7, 8, 9 },
};

控制流

条件语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const x: i32 = 10;

if (x > 5) {
std.debug.print("x is greater than 5\n", .{});
} else if (x == 5) {
std.debug.print("x equals 5\n", .{});
} else {
std.debug.print("x is less than 5\n", .{});
}

// if 作为表达式
const y = if (x > 5) 100 else 0;

// 可选类型的 if
const maybe_value: ?i32 = 42;
if (maybe_value) |value| {
std.debug.print("Value: {}\n", .{value});
} else {
std.debug.print("No value\n", .{});
}

循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// while 循环
var i: i32 = 0;
while (i < 5) : (i += 1) {
std.debug.print("{} ", .{i});
}

// 带 continue 表达式的 while
var j: i32 = 0;
while (j < 10) : (j += 2) {
if (j == 4) continue;
std.debug.print("{} ", .{j});
}

// for 循环(用于数组、切片)
const arr = [_]i32{ 1, 2, 3, 4, 5 };
for (arr) |elem| {
std.debug.print("{} ", .{elem});
}

// 带索引的 for
for (arr, 0..) |elem, idx| {
std.debug.print("[{}] = {}\n", .{ idx, elem });
}

// 带标签的循环(用于 break/continue 到外层循环)
outer: for (0..3) |a| {
for (0..3) |b| {
if (a + b == 3) break :outer;
}
}

switch 语句

Zig 的 switch 是详尽的(exhaustive):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const number = 5;

const result = switch (number) {
1 => "one",
2 => "two",
3 => "three",
4...10 => "between 4 and 10",
else => "other",
};

// switch 与枚举
const Color = enum { red, green, blue };
const color = Color.red;

const color_name = switch (color) {
.red => "Red",
.green => "Green",
.blue => "Blue",
};

函数

函数定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 基本函数
fn add(a: i32, b: i32) i32 {
return a + b;
}

// 无返回值(void)
fn printHello() void {
std.debug.print("Hello!\n", .{});
}

// 多返回值(通过结构体或元组)
fn divide(dividend: i32, divisor: i32) struct { quotient: i32, remainder: i32 } {
return .{
.quotient = @divTrunc(dividend, divisor),
.remainder = @rem(dividend, divisor),
};
}

递归函数

1
2
3
4
fn factorial(n: u32) u32 {
if (n <= 1) return 1;
return n * factorial(n - 1);
}

函数指针

1
2
3
4
5
6
7
8
9
10
11
12
const MathOp = fn (a: i32, b: i32) i32;

fn calculate(a: i32, b: i32, op: MathOp) i32 {
return op(a, b);
}

fn multiply(x: i32, y: i32) i32 {
return x * y;
}

// 使用
const result = calculate(5, 3, multiply); // 15

结构体

基本结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const Point = struct {
x: f32,
y: f32,

// 方法
pub fn distance(self: Point, other: Point) f32 {
const dx = self.x - other.x;
const dy = self.y - other.y;
return std.math.sqrt(dx * dx + dy * dy);
}
};

// 创建实例
const p1 = Point{ .x = 0, .y = 0 };
const p2 = Point{ .x = 3, .y = 4 };
const dist = p1.distance(p2); // 5.0

默认字段值

1
2
3
4
5
6
7
8
const Config = struct {
host: []const u8 = "localhost",
port: u16 = 8080,
debug: bool = false,
};

const default_config = Config{}; // 使用所有默认值
const custom_config = Config{ .port = 3000 }; // 覆盖特定字段

自引用结构体

1
2
3
4
const Node = struct {
value: i32,
next: ?*Node = null,
};

联合体与枚举

枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const Status = enum {
pending,
running,
completed,
failed,

// 枚举方法
pub fn isDone(self: Status) bool {
return self == .completed or self == .failed;
}
};

// 带关联值的枚举(类似 Rust 的枚举)
const Message = union(enum) {
text: []const u8,
number: i32,
quit: void,
};

联合体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Value = union {
int: i32,
float: f64,
boolean: bool,
};

var v = Value{ .int = 42 };
// v.float = 3.14; // 运行时错误:当前激活的字段是 int

// 标签联合体(安全的联合体)
const TaggedValue = union(enum) {
int: i32,
float: f64,
boolean: bool,
};

错误处理

Zig 使用错误联合类型(Error Union Types)进行错误处理:

定义错误

1
2
3
4
5
const FileError = error{
NotFound,
PermissionDenied,
OutOfMemory,
};

错误联合类型

1
2
3
4
5
6
7
8
9
10
11
12
13
// 可能返回 i32 或 FileError
fn mightFail(condition: bool) FileError!i32 {
if (!condition) {
return FileError.NotFound;
}
return 42;
}

// 捕获错误
const result = mightFail(true) catch |err| {
std.debug.print("Error: {}\n", .{err});
return;
};

try 和 catch

1
2
3
4
5
6
7
8
9
10
11
// try:如果出错则立即返回错误
const value = try mightFail(true);

// catch:处理错误
const value_or_default = mightFail(false) catch 0;

// catch 带错误信息
const value2 = mightFail(false) catch |err| blk: {
std.debug.print("Got error: {}\n", .{err});
break :blk -1;
};

if-else 处理错误

1
2
3
4
5
if (mightFail(true)) |value| {
std.debug.print("Success: {}\n", .{value});
} else |err| {
std.debug.print("Failed: {}\n", .{err});
}

可选类型(Optionals)

Zig 使用 ?T 表示类型 T 的可选值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 可选类型
const maybe_number: ?i32 = 42;
const no_number: ?i32 = null;

// if 解构
if (maybe_number) |n| {
std.debug.print("Number: {}\n", .{n});
}

// while 解构(常用于迭代器模式)
var iter = RangeIterator{ .current = 0, .end = 5 };
while (iter.next()) |value| {
std.debug.print("{} ", .{value});
}

// orelse 提供默认值
const value = maybe_number orelse 0;

// .? 操作符(断言非空,不安全)
const definite = maybe_number.?;

编译时计算(comptime)

Zig 的强大特性之一是在编译时执行代码:

comptime 关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 编译时变量
comptime var count: i32 = 0;

// 编译时函数参数
fn compileTimeFunction(comptime T: type, comptime size: usize) type {
return struct {
data: [size]T,

pub fn get(idx: usize) T {
return data[idx];
}
};
}

// 使用
const IntArray10 = compileTimeFunction(i32, 10);
var my_array: IntArray10 = undefined;

类型作为参数

1
2
3
4
5
6
fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}

const m1 = max(i32, 5, 10); // i32 版本
const m2 = max(f64, 3.14, 2.71); // f64 版本

编译时反射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const std = @import("std");

fn printStructInfo(comptime T: type) void {
const info = @typeInfo(T);
std.debug.print("Type: {s}\n", .{@typeName(T)});

if (info == .Struct) {
inline for (info.Struct.fields) |field| {
std.debug.print(" Field: {s} ({s})\n", .{
field.name,
@typeName(field.type),
});
}
}
}

内存管理

Zig 不提供垃圾回收,需要显式管理内存:

分配器模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const std = @import("std");

pub fn main() !void {
// 获取 GPA(通用分配器)
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();

// 动态分配数组
const arr = try allocator.alloc(i32, 10);
defer allocator.free(arr);

// 使用数组
for (arr, 0..) |*item, i| {
item.* = @intCast(i);
}
}

固定缓冲区分配器

1
2
3
4
5
6
var buffer: [1024]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fba.allocator();

// 在小缓冲区上分配,无堆分配
const slice = try allocator.alloc(u8, 100);

Arena 分配器

1
2
3
4
5
6
7
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();

// 所有内存一起释放
_ = try allocator.alloc(u8, 100);
_ = try allocator.alloc(u8, 200);

指针

基本指针

1
2
3
4
5
6
var x: i32 = 42;
const ptr: *i32 = &x; // 单一项指针
ptr.* = 100; // 解引用

// 可选指针
const maybe_ptr: ?*i32 = &x;

切片指针

1
2
3
4
5
6
var arr = [_]i32{ 1, 2, 3, 4, 5 };
const slice: []i32 = &arr; // 切片
const const_slice: []const i32 = &arr; // 常量切片

// 哨兵终止切片(以特定值结尾)
const str: [:0]const u8 = "hello"; // 以 0 结尾

多指针

1
2
3
4
5
// [*]T - 未知长度的多指针(类似 C 指针)
// [*c]T - C 风格的指针

// 示例:C 互操作
extern fn c_function(ptr: [*c]const u8) void;

与 C 语言互操作

Zig 可以无缝与 C 代码集成:

导入 C 头文件

1
2
3
4
5
6
7
8
const c = @cImport({
@cInclude("stdio.h");
@cInclude("stdlib.h");
});

pub fn main() void {
_ = c.printf("Hello from C!\n");
}

导出 Zig 函数给 C

1
2
3
4
5
6
7
8
export fn zig_add(a: i32, b: i32) i32 {
return a + b;
}

// 使用 C 调用约定
export "C" fn zig_multiply(a: i32, b: i32) i32 {
return a * b;
}

类型映射

1
2
3
4
5
6
7
8
// C 类型映射
const c_int: c_int = 42;
const c_char: c_char = 'a';
const c_void: c_void = undefined;

// C 字符串
const c_str: [*c]const u8 = "hello";
const zig_str: []const u8 = std.mem.span(c_str); // 转换为 Zig 切片

泛型编程

使用 comptime 实现泛型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
fn Stack(comptime T: type) type {
return struct {
const Self = @This();

items: []T,
capacity: usize,
len: usize = 0,
allocator: std.mem.Allocator,

pub fn init(allocator: std.mem.Allocator, capacity: usize) !Self {
const items = try allocator.alloc(T, capacity);
return Self{
.items = items,
.capacity = capacity,
.allocator = allocator,
};
}

pub fn deinit(self: *Self) void {
self.allocator.free(self.items);
}

pub fn push(self: *Self, item: T) !void {
if (self.len >= self.capacity) return error.OutOfCapacity;
self.items[self.len] = item;
self.len += 1;
}

pub fn pop(self: *Self) ?T {
if (self.len == 0) return null;
self.len -= 1;
return self.items[self.len];
}
};
}

// 使用
const IntStack = Stack(i32);
var stack = try IntStack.init(allocator, 10);
defer stack.deinit();

构建系统

Zig 自带构建系统,使用 build.zig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const std = @import("std");

pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

// 可执行文件
const exe = b.addExecutable(.{
.name = "myapp",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});

b.installArtifact(exe);

// 运行命令
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());

const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);

// 测试
const unit_tests = b.addTest(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});

const run_unit_tests = b.addRunArtifact(unit_tests);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_unit_tests.step);
}

测试

Zig 内置测试框架:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const std = @import("std");
const testing = std.testing;

fn add(a: i32, b: i32) i32 {
return a + b;
}

// 测试函数
test "basic addition" {
try testing.expect(add(2, 3) == 5);
try testing.expectEqual(@as(i32, 5), add(2, 3));
}

test "expect error" {
const result = mightFail(false);
try testing.expectError(error.NotFound, result);
}

// 在文档中测试
/// 计算阶乘
/// ```
/// const fact = factorial(5);
/// try std.testing.expect(fact == 120);
/// ```
fn factorial(n: u32) u32 {
if (n <= 1) return 1;
return n * factorial(n - 1);
}

运行测试:

1
zig test file.zig

包管理

Zig 使用 build.zig.zon 管理依赖:

1
2
3
4
5
6
7
8
9
10
.{
.name = "myproject",
.version = "0.1.0",
.dependencies = .{
.zap = .{
.url = "https://github.com/zigzap/zap/archive/refs/tags/v0.1.0.tar.gz",
.hash = "1220...",
},
},
}

最佳实践

  1. 错误处理:始终使用错误联合类型而非返回错误码
  2. 内存管理:使用 defer 确保资源释放
  3. 编译时计算:利用 comptime 进行泛型和元编程
  4. 显式控制:避免隐式行为,所有操作都应清晰可见
  5. 与 C 互操作:使用 Zig 逐步替换 C 代码

总结

Zig 是一门设计精良的系统编程语言,主要特点包括:

  • 显式优于隐式:没有隐藏的控制流或内存分配
  • 编译时代码执行:强大的元编程能力
  • 零成本抽象:高性能的同时保持代码清晰
  • C 语言互操作:无缝集成现有 C 代码
  • 安全第一:通过编译时检查和显式错误处理避免运行时错误

Zig 适合系统编程、嵌入式开发、游戏引擎、编译器等需要高性能和精细控制的场景。

参考资源