概述
@builtins是编译器的动词;它们描述了Zig如何思考类型、指针和程序结构,并且它们在每个文件中都可用,无需导入。在第三部分尝试了编译时编程之后,本附录捕获了最常见的内置函数、它们的意图,以及在阅读或编写元编程重的Zig时应该记住的表面层契约。15
0.15.2版本稳定了几个内省辅助函数(@typeInfo、@hasDecl、@field)并澄清了新整数大小的截断语义,使得依赖这里总结的行为变得实用。v0.15.2
学习目标
- 在扫描代码库时发现反射内置函数、算术辅助函数和控制内置函数之间的差异。
- 结合类型检查内置函数来构建与用户提供类型一起工作的适配器。
- 在范围和安全模式的边缘验证数值转换的运行时行为。
核心反射内置函数
反射内置函数为我们提供关于用户类型的结构化信息,而不获取原始指针或丢弃安全检查。15 下面的示例显示如何形成任何struct的文档化摘要,包括comptime字段、可选有效负载和嵌套数组。
//! Summarizes struct metadata using @typeInfo and @field.
const std = @import("std");
fn describeStruct(comptime T: type, writer: anytype) !void {
const info = @typeInfo(T);
switch (info) {
.@"struct" => |struct_info| {
try writer.print("struct {s} has {d} fields", .{ @typeName(T), struct_info.fields.len });
inline for (struct_info.fields, 0..) |field, index| {
try writer.print("\n {d}: {s} : {s}", .{ index, field.name, @typeName(field.type) });
}
},
else => try writer.writeAll("not a struct"),
}
}
test "describe struct reports field metadata" {
const Sample = struct {
id: u32,
value: ?f64,
};
var buffer: [256]u8 = undefined;
var stream = std.io.fixedBufferStream(&buffer);
try describeStruct(Sample, stream.writer());
const summary = stream.getWritten();
try std.testing.expect(std.mem.containsAtLeast(u8, summary, 1, "id"));
try std.testing.expect(std.mem.containsAtLeast(u8, summary, 1, "value"));
}
test "describe struct rejects non-struct types" {
var buffer: [32]u8 = undefined;
var stream = std.io.fixedBufferStream(&buffer);
try describeStruct(u8, stream.writer());
const summary = stream.getWritten();
try std.testing.expectEqualStrings("not a struct", summary);
}
$ zig test 01_struct_introspection.zigAll 2 tests passed.在内联循环内使用@typeInfo加上@field,以便编译器在特化后仍然优化掉分支。17
值提取辅助函数
诸如@field、@hasField和@fieldParentPtr之类的内置函数让您可以将运行时数据映射回编译时声明,而不违反Zig的严格别名规则。以下片段显示如何在保持const正确性的同时暴露父指针。meta.zig
//! Demonstrates `@fieldParentPtr` to recover container pointers safely.
const std = @import("std");
const Node = struct {
id: u32,
payload: Payload,
};
const Payload = struct {
node_ptr: *const Node,
value: []const u8,
};
fn makeNode(id: u32, value: []const u8) Node {
var node = Node{
.id = id,
.payload = undefined,
};
node.payload = Payload{
.node_ptr = &node,
.value = value,
};
return node;
}
test "parent pointer recovers owning node" {
var node = makeNode(7, "ready");
const parent: *const Node = @fieldParentPtr("payload", &node.payload);
try std.testing.expectEqual(@as(u32, 7), parent.id);
}
test "field access respects const rules" {
var node = makeNode(3, "go");
const parent: *const Node = @fieldParentPtr("payload", &node.payload);
try std.testing.expectEqualStrings("go", parent.payload.value);
}
$ zig test 02_parent_ptr_lookup.zigAll 2 tests passed.@fieldParentPtr假设子指针有效且正确对齐;在调试构建中将其与std.debug.assert结合使用,以尽早捕获意外误用。37
数值安全内置函数
数值转换是未定义行为经常隐藏的地方;Zig通过@intCast、@intFromFloat和@truncate使截断显式化,这些都遵循安全模式语义。37 0.15.2改进了这些内置函数在溢出时发出的诊断,使它们在调试构建中成为可靠的守护者。
//! Exercises numeric conversion builtins with guarded tests.
const std = @import("std");
fn toU8Lossy(value: u16) u8 {
return @truncate(value);
}
fn toI32(value: f64) i32 {
return @intFromFloat(value);
}
fn widenU16(value: u8) u16 {
return @intCast(value);
}
test "truncate discards high bits" {
try std.testing.expectEqual(@as(u8, 0x34), toU8Lossy(0x1234));
}
test "intFromFloat matches floor for positive range" {
try std.testing.expectEqual(@as(i32, 42), toI32(42.9));
}
test "intCast widens without loss" {
try std.testing.expectEqual(@as(u16, 255), widenU16(255));
}
$ zig test 03_numeric_conversions.zigAll 3 tests passed.将有损转换包装在小辅助函数中,以便意图保持可读,并且您可以在共享数字逻辑周围集中断言。10
编译时控制和守护
@compileError、@panic、@setEvalBranchQuota和@inComptime让您直接控制编译时执行;它们是保持元编程确定性和透明的安全阀。15 下面的小示例在编译时守护向量宽度,并在分析期间计算小斐波那契数之前提高评估分支配额。
//! Demonstrates compile-time guards using @compileError and @setEvalBranchQuota.
const std = @import("std");
fn ensureVectorLength(comptime len: usize) type {
if (len < 2) {
@compileError("invalid vector length; expected at least 2 lanes");
}
return @Vector(len, u8);
}
fn boundedFib(comptime quota: u32, comptime n: u32) u64 {
@setEvalBranchQuota(quota);
return comptimeFib(n);
}
fn comptimeFib(comptime n: u32) u64 {
if (n <= 1) return n;
return comptimeFib(n - 1) + comptimeFib(n - 2);
}
test "guard accepts valid size" {
const Vec = ensureVectorLength(4);
const info = @typeInfo(Vec);
try std.testing.expectEqual(@as(usize, 4), info.vector.len);
// Uncommenting the next line triggers the compile-time guard:
// const invalid = ensureVectorLength(1);
}
test "branch quota enables deeper recursion" {
const result = comptime boundedFib(1024, 12);
try std.testing.expectEqual(@as(u64, 144), result);
}
$ zig test 04_comptime_guards.zigAll 2 tests passed.@compileError立即停止编译单元;谨慎使用它,并倾向于在运行时验证更便宜时返回错误。留下一个注释掉的调用(如示例中)来记录失败模式而不破坏构建。12