# std.meta Comptime type introspection and manipulation utilities. Essential for generic programming, serialization, and metaprogramming. ## Quick Reference | Function | Purpose | |----------|---------| | `stringToEnum(T, str)` | Convert string to enum variant | | `fields(T)` | Get struct/union/enum or concrete error-set fields | | `fieldNames(T)` | Get field names as string slice | | `fieldInfo(T, field)` | Get info for specific field | | `fieldIndex(T, name)` | Get field index by name | | `tags(T)` | Get all enum/error set values | | `Tag(T)` | Get tag type of enum/union | | `activeTag(u)` | Get active variant of tagged union | | `eql(a, b)` | Deep equality comparison | | `Child(T)` | Get child type of pointer/array/optional | | `Elem(T)` | Get element type of memory span | | `sentinel(T)` | Get sentinel value if any | | `FieldEnum(T)` | Generate enum from field names | | `hasFn(T, name)` | Check if type has function | | `hasMethod(T, name)` | Check if type has method | ## String to Enum Conversion ```zig const std = @import("std"); const Color = enum { red, green, blue }; // Convert runtime string to enum const color = std.meta.stringToEnum(Color, "green"); if (color) |c| { // c == Color.green } // Returns null for invalid strings const invalid = std.meta.stringToEnum(Color, "purple"); // null ``` Uses `StaticStringMap` for small enums (≤100 variants), inline iteration for larger ones. ## Field Introspection ### Get All Fields ```zig const Point = struct { x: f32, y: f32, z: f32 = 0, }; // Get struct fields const point_fields = std.meta.fields(Point); // point_fields.len == 3 // point_fields[0].name == "x" // point_fields[0].type == f32 // Works for unions const Result = union { ok: u32, err: []const u8 }; const union_fields = std.meta.fields(Result); // Works for enums const Status = enum { pending, done }; const enum_fields = std.meta.fields(Status); // Works for error sets const MyError = error{ NotFound, Timeout }; const error_fields = std.meta.fields(MyError); ``` `fields` cannot enumerate the global `anyerror` set; use it only with a concrete error set whose members are known. ### Get Field Names ```zig const names = std.meta.fieldNames(Point); // names.* == .{ "x", "y", "z" } for (names) |name| { std.debug.print("{s}\n", .{name}); } ``` ### Get Specific Field ```zig // By enum literal const x_info = std.meta.fieldInfo(Point, .x); // x_info.name == "x" // x_info.type == f32 // x_info.default_value_ptr == null const z_info = std.meta.fieldInfo(Point, .z); // z_info.default_value_ptr != null (has default 0) // Get field index const idx = std.meta.fieldIndex(Point, "y"); // 1 const bad = std.meta.fieldIndex(Point, "w"); // null ``` ## Enum/Union Tag Operations ### Get Tag Type ```zig const Status = enum(u8) { active = 1, inactive = 2 }; const TagInt = std.meta.Tag(Status); // u8 const Tagged = union(enum) { a: u32, b: f32 }; const TagEnum = std.meta.Tag(Tagged); // enum { a, b } ``` ### Get Active Tag ```zig const Value = union(enum) { int: i32, float: f32 }; var v = Value{ .int = 42 }; const tag = std.meta.activeTag(v); // Value.int switch (tag) { .int => std.debug.print("integer\n", .{}), .float => std.debug.print("float\n", .{}), } ``` ### Get All Tags ```zig const Color = enum { red, green, blue }; const colors = std.meta.tags(Color); // colors.* == .{ Color.red, Color.green, Color.blue } const MyError = error{ A, B }; const errors = std.meta.tags(MyError); // errors.* == .{ MyError.A, MyError.B } ``` ## Type Construction ### FieldEnum - Generate Enum from Fields ```zig const Point = struct { x: f32, y: f32 }; const PointField = std.meta.FieldEnum(Point); // Equivalent to: enum { x, y } // Iterate fields generically inline for (std.meta.tags(PointField)) |field| { const info = std.meta.fieldInfo(Point, field); std.debug.print("{s}: {s}\n", .{ info.name, @typeName(info.type) }); } ``` For a tagged union, `FieldEnum` reuses the existing tag enum only when field names match in order and the tag values are consecutive from zero; otherwise it constructs a separate enum. For tagged unions, returns the existing tag type if compatible. ### DeclEnum - Generate Enum from Declarations ```zig const Api = struct { pub fn getUser() void {} pub fn deleteUser() void {} }; const ApiMethod = std.meta.DeclEnum(Api); // Equivalent to: enum { getUser, deleteUser } ``` ### Int/Float Type Construction ```zig const U24 = @Int(.unsigned, 24); // u24 const I7 = @Int(.signed, 7); // i7 const F32 = std.meta.Float(32); // f32 const F16 = std.meta.Float(16); // f16 ``` ### Tuple Construction ```zig // From type array const T1 = std.meta.Tuple(&.{ u32, f32, bool }); // deprecated compatibility helper // Equivalent to: struct { u32, f32, bool } // From function signature const T2 = std.meta.ArgsTuple(fn (u32, f32) void); // Equivalent to: struct { u32, f32 } ``` ## Child/Element Types ### Child - Direct Child Type ```zig std.meta.Child(*u8) // u8 std.meta.Child([]u8) // u8 std.meta.Child([5]u8) // u8 std.meta.Child(?u8) // u8 std.meta.Child(@Vector(4, f32)) // f32 ``` ### Elem - Element Type of Memory Spans ```zig std.meta.Elem([5]u8) // u8 std.meta.Elem([]u8) // u8 std.meta.Elem([*]u8) // u8 std.meta.Elem(*[10]u8) // u8 (through pointer to array) std.meta.Elem(?[*]u8) // u8 (through optional) ``` ### Sentinel ```zig const slice_sentinel = std.meta.sentinel([:0]u8).?; // @as(u8, 0) const ptr_sentinel = std.meta.sentinel([*:0]u8).?; // @as(u8, 0) const array_sentinel = std.meta.sentinel([5:0]u8).?; // @as(u8, 0) std.meta.sentinel([]u8) // null std.meta.sentinel([5]u8) // null ``` ### Sentinel Type Construction ```zig // Add sentinel to type const S1 = std.meta.Sentinel([]u8, 0); // [:0]u8 const S2 = std.meta.Sentinel([*]u8, 0); // [*:0]u8 ``` ## Deep Equality ```zig const std = @import("std"); const Point = struct { x: i32, y: i32 }; const p1 = Point{ .x = 1, .y = 2 }; const p2 = Point{ .x = 1, .y = 2 }; const p3 = Point{ .x = 1, .y = 3 }; std.meta.eql(p1, p2) // true std.meta.eql(p1, p3) // false // Works with nested structs, arrays, optionals, error unions const Complex = struct { data: [3]u8, opt: ?i32, }; const a = Complex{ .data = .{ 1, 2, 3 }, .opt = 42 }; const b = Complex{ .data = .{ 1, 2, 3 }, .opt = 42 }; std.meta.eql(a, b) // true // Pointers compared by address, not content std.meta.eql(&p1, &p2) // false (different addresses) std.meta.eql(&p1, &p1) // true ``` **Supported types:** structs, arrays, vectors, optionals, error unions, tagged unions, primitives. Non-packed untagged unions cannot be compared generically because there is no active tag to select a field. Packed unions are the exception handled as their packed representation. **Not supported:** untagged unions (compile error). ## Type Queries ### Check for Function/Method ```zig const S = struct { value: u32, pub fn method(self: *@This()) void { _ = self; } }; std.meta.hasFn(S, "method") // true std.meta.hasFn(S, "value") // false (field, not fn) std.meta.hasFn(S, "missing") // false std.meta.hasMethod(S, "method") // true std.meta.hasMethod(*S, "method") // true (through pointer) std.meta.hasMethod([]S, "method") // false (slice, not single pointer) ``` ### Check Unique Representation ```zig // True if type has no padding/unused bits std.meta.hasUniqueRepresentation(u8) // true std.meta.hasUniqueRepresentation(u32) // true std.meta.hasUniqueRepresentation(i24) // false (padded to 4 bytes) // Struct with no padding const Packed = struct { a: u32, b: u32 }; std.meta.hasUniqueRepresentation(Packed) // true // Struct with padding const Padded = struct { a: u32, b: u16 }; std.meta.hasUniqueRepresentation(Padded) // false ``` ### Container Layout ```zig const Auto = struct {}; const Packed = packed struct {}; const Extern = extern struct {}; std.meta.containerLayout(Auto) // .auto std.meta.containerLayout(Packed) // .@"packed" std.meta.containerLayout(Extern) // .@"extern" ``` ### Alignment ```zig // For pointers, returns the pointed-to alignment (not pointer alignment) std.meta.alignment(*align(16) u8) // 16 std.meta.alignment([]align(8) u8) // 8 std.meta.alignment(u8) // 1 ``` ### Declarations ```zig const S = struct { pub const value = 42; pub fn method() void {} }; const decls = std.meta.declarations(S); // decls[0].name == "method" or "value" ``` ## Error Handling ```zig // Inspect an error union with ordinary error-union syntax. const result = std.math.divTrunc(u8, 5, 0); if (result) |value| { _ = value; } else |err| { _ = err; } // Checked enum conversion returns an optional. const Color = enum { red, green, blue }; const c = std.enums.fromInt(Color, 1) orelse return error.InvalidColor; ``` ## TrailerFlags Memory-efficient optional field storage using bit flags: ```zig const std = @import("std"); const Trailer = struct { name: []const u8, age: u32, email: []const u8, }; const Flags = std.meta.TrailerFlags(Trailer); // Initialize with some fields active var flags = Flags.init(.{ .name = true, .age = true, .email = false, }); // Allocate only needed space const size = flags.sizeInBytes(); const data = try allocator.alignedAlloc(u8, .of(Trailer), size); defer allocator.free(data); // Set values flags.set(data.ptr, .name, "Alice"); flags.set(data.ptr, .age, 30); // Get values (returns optional) const name = flags.get(data.ptr, .name); // ?"Alice" const email = flags.get(data.ptr, .email); // null // Set multiple at once flags.setMany(data.ptr, .{ .name = "Bob", .age = 25, }); ``` Use case: Allocating objects with many optional components where memory matters. ## Generic Programming Patterns ### Iterate All Fields ```zig fn printStruct(value: anytype) void { const T = @TypeOf(value); inline for (std.meta.fields(T)) |field| { const v = @field(value, field.name); std.debug.print("{s}: {any}\n", .{ field.name, v }); } } ``` ### Create Default Instance ```zig fn createDefault(comptime T: type) T { var result: T = undefined; inline for (std.meta.fields(T)) |field| { if (field.default_value_ptr) |ptr| { @field(result, field.name) = @as(*const field.type, @ptrCast(@alignCast(ptr))).*; } else { @field(result, field.name) = std.mem.zeroes(field.type); } } return result; } ``` ### Dynamic Field Access ```zig fn getField(comptime T: type, value: T, comptime name: []const u8) ?std.meta.fieldInfo(T, std.meta.stringToEnum(std.meta.FieldEnum(T), name) orelse return null).type { const field = std.meta.stringToEnum(std.meta.FieldEnum(T), name) orelse return null; return @field(value, @tagName(field)); } ``` ### Serialize to JSON-like ```zig fn toJson(value: anytype, writer: anytype) !void { const T = @TypeOf(value); switch (@typeInfo(T)) { .@"struct" => |info| { try writer.writeAll("{"); inline for (info.fields, 0..) |field, i| { if (i > 0) try writer.writeAll(","); try writer.print("\"{s}\":", .{field.name}); try toJson(@field(value, field.name), writer); } try writer.writeAll("}"); }, .int, .float => try writer.print("{}", .{value}), .pointer => |ptr| if (ptr.size == .slice and ptr.child == u8) { try writer.print("\"{s}\"", .{value}); }, else => try writer.writeAll("null"), } } ```