Skip to content

Zig 函数教程

// 基本函数声明
fn add(a: i8, b: i8) i8 {
return a + b;
}
// 带条件返回
fn max(a: i32, b: i32) i32 {
if (a > b) {
return a;
}
return b;
}
// 其他文件可以通过 @import 导入并使用
pub fn multiply(x: i32, y: i32) i32 {
return x * y;
}
// 在生成的目标文件中外部可见,使用 C ABI
export fn subtract(a: i32, b: i32) i32 {
return a - b;
}
// 其他语言(如C)可以调用此函数
// 声明来自外部库的函数
// 静态链接:链接时解析
// 动态链接:运行时解析
// 来自 libc 的函数
extern "c" fn printf(format: [*:0]const u8, ...) c_int;
// Windows API 函数
extern "kernel32" fn ExitProcess(exit_code: u32) callconv(.winapi) noreturn;
// 指定调用约定
extern "c" fn atan2(a: f64, b: f64) f64;

永不返回的函数:

fn abort() noreturn {
while (true) {}
// 不需要 return 语句
}
fn panic(message: []const u8) noreturn {
std.debug.print("Panic: {s}\n", .{message});
while (true) {}
}
fn logError(message: []const u8) void {
@branchHint(.cold); // 提示编译器此函数很少被调用
std.debug.print("ERROR: {s}\n", .{message});
}
// 无函数序言和结语,直接汇编代码
fn _start() callconv(.naked) noreturn {
// 通常用于引导代码或汇编集成
asm volatile ("syscall"
:
: [number] "{rax}" (60), // exit 系统调用号
[arg1] "{rdi}" (0) // 退出码
: "rcx", "r11", "memory"
);
unreachable;
}
// 强制在所有调用点内联,不能内联则编译错误
inline fn double(x: u32) u32 {
return x * 2;
}
// 实际使用:所有调用点都会展开此函数
const result = double(5); // 编译为:const result = 5 * 2;

使用场景:

  1. 调试时控制调用栈深度
  2. 强制编译期参数传递到返回值
  3. 性能测试明确需要内联

注意: 过度使用可能损害性能、增加二进制大小、减慢编译速度。

fn increment(x: i32) i32 {
// x 是原始值的副本
return x + 1;
}
test "pass by value" {
const original = 5;
const result = increment(original);
// original 仍然是 5
// result 是 6
}

4.2 复合类型:传值或传引用(编译器决定)

Section titled “4.2 复合类型:传值或传引用(编译器决定)”
const Point = struct {
x: i32,
y: i32,
};
fn sumCoordinates(p: Point) i32 {
// p 可能是副本,也可能是引用
// 参数不可变,编译器可以优化
return p.x + p.y;
}
test "pass struct" {
const point = Point{ .x = 10, .y = 20 };
const result = sumCoordinates(point);
try expect(result == 30);
}

重要警告: 不要获取参数地址,函数返回后地址可能失效。

// 对于 extern 函数,Zig 遵循 C ABI 传递结构体和联合体
extern "c" fn process_point(p: Point) i32;
// 如果 Point 在 C 中按值传递,Zig 也会按值传递
fn addFortyTwo(x: anytype) @TypeOf(x) {
// x 的类型在调用时推断
// 返回类型与 x 相同
return x + 42;
}
test "type inference" {
// 调用时推断类型
const a = addFortyTwo(1); // a: comptime_int
const b = addFortyTwo(@as(i64, 2)); // b: i64
const c = addFortyTwo(@as(f32, 3.0)); // c: f32
try expect(a == 43);
try expect(b == 44);
try expect(c == 45.0);
}
fn genericToString(value: anytype) []const u8 {
const T = @TypeOf(value);
return switch (@typeInfo(T)) {
.Int => std.fmt.allocPrint(std.heap.page_allocator, "{}", .{value}),
.Float => std.fmt.allocPrint(std.heap.page_allocator, "{d}", .{value}),
.Bool => if (value) "true" else "false",
else => @compileError("Unsupported type"),
};
}
inline fn calculate(comptime a: i32, b: i32) i32 {
// a 必须是编译期已知
std.debug.print("a = {} (compile-time), b = {} (runtime)\n", .{a, b});
return a + b;
}
pub fn main() void {
// 由于 inline,1200 作为编译期参数传播
const result = calculate(1200, 34); // 编译时部分计算
if (result != 1234) {
@compileError("bad"); // 编译期检查
}
}
const Mode = enum { Debug, Release };
inline fn createBuffer(comptime mode: Mode) []u8 {
const size = switch (mode) {
.Debug => 1024, // 调试模式下更大缓冲区
.Release => 256, // 发布模式下更小
};
return std.heap.page_allocator.alloc(u8, size) catch unreachable;
}
// 使用
const debug_buffer = createBuffer(.Debug); // 1024 字节
const release_buffer = createBuffer(.Release); // 256 字节
// 函数指针类型
const MathOp = *const fn (a: i32, b: i32) i32;
fn add(a: i32, b: i32) i32 { return a + b; }
fn multiply(a: i32, b: i32) i32 { return a * b; }
fn doOperation(op: MathOp, x: i32, y: i32) i32 {
return op(x, y);
}
test "function pointers" {
try expect(doOperation(add, 3, 4) == 7);
try expect(doOperation(multiply, 3, 4) == 12);
}
// Zig 没有闭包,但可以使用结构体模拟
const Adder = struct {
increment: i32,
fn call(self: Adder, x: i32) i32 {
return x + self.increment;
}
};
const AdderFn = *const fn (context: *Adder, x: i32) i32;
fn makeAdder(increment: i32) Adder {
return Adder{ .increment = increment };
}
const std = @import("std");
const testing = std.testing;
test "function reflection" {
// 获取函数类型信息
const ExpectFn = @TypeOf(testing.expect);
const fn_info = @typeInfo(ExpectFn).@"fn";
// 检查参数
try expect(fn_info.params.len >= 1);
try expect(fn_info.params[0].type.? == bool);
// 检查返回类型
try expect(fn_info.return_type.? == void);
// 检查是否是泛型函数
const Log2IntFn = @TypeOf(std.math.Log2Int);
try expect(@typeInfo(Log2IntFn).@"fn".is_generic == true);
}
fn analyzeFunction(comptime Func: type) void {
const info = @typeInfo(Func);
switch (info) {
.@"fn" => |fn_info| {
std.debug.print("Function info:\n", .{});
std.debug.print(" Return type: {}\n", .{fn_info.return_type});
std.debug.print(" Is generic: {}\n", .{fn_info.is_generic});
std.debug.print(" Calling convention: {}\n", .{fn_info.calling_convention});
for (fn_info.params, 0..) |param, i| {
std.debug.print(" Param {}: {?s} : {?}\n", .{
i, param.name, param.type
});
}
},
else => @compileError("Not a function type"),
}
}
// 使用
analyzeFunction(@TypeOf(std.mem.copy));
fn map(
comptime T: type,
data: []const T,
comptime mapper: fn (T) T
) []T {
const result = std.heap.page_allocator.alloc(T, data.len);
for (data, 0..) |item, i| {
result[i] = mapper(item);
}
return result;
}
fn square(x: i32) i32 { return x * x; }
fn double(x: i32) i32 { return x * 2; }
test "higher-order function" {
const data = [_]i32{1, 2, 3, 4};
const squared = map(i32, &data, square);
const doubled = map(i32, &data, double);
// 清理内存...
}
const EventHandler = *const fn (context: *anyopaque, data: *const EventData) void;
const EventSystem = struct {
handlers: std.ArrayList(EventHandler),
contexts: std.ArrayList(*anyopaque),
fn register(self: *EventSystem, handler: EventHandler, context: *anyopaque) void {
self.handlers.append(handler);
self.contexts.append(context);
}
fn trigger(self: *EventSystem, data: *const EventData) void {
for (self.handlers.items, self.contexts.items) |handler, context| {
handler(context, data);
}
}
};
  1. 默认使用普通函数,让编译器决定是否内联
  2. 谨慎使用 inline,只在明确需要时
  3. 优先使用具体类型,类型推断保留给泛型代码
  4. 合理使用函数指针,但注意 Zig 没有闭包
  5. 遵循 C ABI 进行跨语言交互
  6. 利用反射进行元编程,但要保持可读性
  7. 注意参数传递语义,避免不必要的复制
const std = @import("std");
// 公开 API 函数
pub fn create_vector(x: f32, y: f32, z: f32) Vector {
return Vector{ .x = x, .y = y, .z = z };
}
// 内部辅助函数
fn dot_product(a: Vector, b: Vector) f32 {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
// 泛型数学函数
fn lerp(comptime T: type, a: T, b: T, t: f32) T {
return a + (b - a) * t;
}
// C 导出接口
export fn compute_length(vec: *const Vector) f32 {
return std.math.sqrt(dot_product(vec.*, vec.*));
}
// 回调系统
pub const Callback = struct {
func: *const fn (data: []const u8) void,
user_data: *anyopaque,
};
pub fn register_callback(cb: Callback) void {
// 注册逻辑...
}
const Vector = struct {
x: f32,
y: f32,
z: f32,
};
test "function features" {
const v1 = create_vector(1.0, 2.0, 3.0);
const v2 = create_vector(4.0, 5.0, 6.0);
const dot = dot_product(v1, v2);
try expect(dot == 32.0); // 1*4 + 2*5 + 3*6 = 32
const result = lerp(f32, 10.0, 20.0, 0.5);
try expect(result == 15.0);
}

通过掌握 Zig 函数的各种特性,你可以编写出高效、类型安全且易于维护的代码。