概览
@builtins是编译器的动词;它们描述 Zig 如何看待类型、指针与程序结构,并且无需导入即可在每个文件使用。在第三部分体验了编译期编程之后,本附录汇总最常见的内建函数、其意图与在重度元编程代码中应牢记的表层契约。15
0.15.2 稳定了多项内省助手(@typeInfo、@hasDecl、@field),并明确了新整数尺寸的截断语义,因此依赖此处总结的行为在实践中更可行。v0.15.2
学习目标
- 扫描代码库时识别反射内建、算术助手和控制内建之间的区别。
- 结合类型检查内建构建与用户提供的类型一起工作的适配器。
- 验证数值转换在范围和安全模式边缘处的运行时行为。
核心反射内建
反射内建为我们提供关于用户类型的结构化信息,而无需获取原始指针或丢弃安全检查。15 下面的示例展示如何形成任何结构的文档化摘要,包括编译时字段、可选有效负载和嵌套数组。
// ! 使用 @typeInfo 和 @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 的严格别名规则。以下片段展示如何在保持常量正确性的同时公开父指针。meta.zig
// ! 演示使用 `@fieldParentPtr` 安全恢复容器指针。
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 改进了这些内建在溢出时发出的诊断,使它们在调试构建中成为可靠的守护者。
// ! 通过受保护的测试练习数字转换内置函数。
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
// ! 演示使用 @compileError 和 @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);
// 取消注释下一行将触发编译时守卫:
// 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