Reflection and Type Introspection
26.1 @typeInfo and @Type
std.builtin.Type tags: .int, .float, .pointer, .array, .@"struct", .@"enum", .@"union", .optional, .error_union, .error_set, .@"fn", .vector, .@"opaque", .bool, .void, .type, .comptime_int, ... (lowercase since 0.14).
const User = struct {
name: []const u8,
age: u32,
active: bool = true,
};
fn printFields(comptime T: type) void {
const info = @typeInfo(T);
switch (info) {
.@"struct" => |s| {
inline for (s.fields) |field| {
std.debug.print("{s}: {s}\n", .{ field.name, @typeName(field.type) });
}
},
else => @compileError("not a struct"),
}
}
fn dumpValue(value: anytype) void {
inline for (std.meta.fields(@TypeOf(value))) |f| {
std.debug.print("{s} = {any}\n", .{ f.name, @field(value, f.name) }); // dynamic field access
}
}26.2 Constructing Types with @Type
fn MakeOptionalFields(comptime T: type) type {
const fields = std.meta.fields(T);
var new_fields: [fields.len]std.builtin.Type.StructField = undefined;
for (fields, 0..) |f, i| {
new_fields[i] = .{
.name = f.name,
.type = ?f.type,
.default_value_ptr = &@as(?f.type, null),
.is_comptime = false,
.alignment = @alignOf(?f.type),
};
}
return @Type(.{ .@"struct" = .{
.layout = .auto,
.fields = &new_fields,
.decls = &.{},
.is_tuple = false,
} });
}
const PartialUser = MakeOptionalFields(User); // every field optional (like TypeScript's Partial)26.3 Reflection Helpers
Builtin / std.meta Type of an expression Type name string @hasField(T, "name"), @hasDecl(T, "name") @field(obj, "name") Access a field by comptime string @FieldType(T, "name") Type of a field (0.14+) Tag as a string
@sizeOf, @alignOf, @bitSizeOf, @offsetOf, @bitOffsetOf
std.meta.fields, std.meta.declarations, std.meta.Tag, std.meta.FieldEnum, std.meta.eql, std.meta.activeTag, std.meta.stringToEnum, std.meta.Child, std.meta.Elem, std.meta.hasFn
Practical uses: serializers (JSON, ZON), ORMs, CLI argument parsers generated from structs, ECS (entity component systems), bindings generators.