पाठ 15 / 25

Reflection with @typeInfo and anytype

Inspect types at compile time to write flexible code.

Code that adapts to types

@TypeOf(x) returns the type of an expression, and @typeInfo(T) returns a description of a type as a tagged union (std.builtin.Type): for a struct, its fields with names, types and defaults; for an enum, its fields and values; for an integer, its signedness and bit count; and so on. Recent Zig versions name these variants with lowercase keywords, written as .@"struct", .@"enum" and .int. Combined with inline for over fields and @field(value, name) to access a field by a compile-time name, this enables compile-time reflection: serialisers, debug printers, argument parsers, ORMs and validation can be written once for any struct, with no run-time reflection cost. anytype parameters accept any type and are specialised per call; use them for flexible APIs, but document the expected shape, and prefer explicit comptime T: type when the type matters for readability. Helpers such as std.meta.fields, std.meta.FieldEnum and std.meta.eql wrap common patterns. Keep reflection-heavy code in libraries, as it can make compile errors harder to read.

A generic struct printer and validator

inline for over fields, @field and @compileError.

const std = @import("std");

fn printStruct(value: anytype) void {
    const T = @TypeOf(value);
    const info = @typeInfo(T);
    if (info != .@"struct") @compileError("printStruct expects a struct, got " ++ @typeName(T));

    std.debug.print("{s} {{\n", .{@typeName(T)});
    inline for (info.@"struct".fields) |field| {
        const v = @field(value, field.name);
        if (field.type == []const u8) {
            std.debug.print("  {s}: \"{s}\"\n", .{ field.name, v });
        } else {
            std.debug.print("  {s}: {any}\n", .{ field.name, v });
        }
    }
    std.debug.print("}}\n", .{});
}

fn countZeroFields(value: anytype) usize {
    var zeros: usize = 0;
    inline for (std.meta.fields(@TypeOf(value))) |field| {
        if (@typeInfo(field.type) == .int) {           // comptime-known: other fields skip this branch
            if (@field(value, field.name) == 0) zeros += 1;
        }
    }
    return zeros;
}

const Product = struct {
    sku: []const u8,
    price_paise: u32,
    stock: u16,
    active: bool,
};

pub fn main() void {
    const p = Product{ .sku = "pen", .price_paise = 4_950, .stock = 0, .active = true };
    printStruct(p);
    std.debug.print("zero integer fields: {d}\n", .{countZeroFields(p)});   // 1
    // printStruct(42);   // compile error: printStruct expects a struct, got comptime_int
}

Escape braces in format strings

Zig's formatter treats { and } as placeholders, so print literal braces as {{ and }}, as in the struct printer above.

त्वरित जाँच: What does @field(value, name) do when name is comptime-known?

  • Creates a new field at run time
  • Deletes a field
  • Accesses the field of value with that name, resolved at compile time
  • Returns the field's type only
Answer

Accesses the field of value with that name, resolved at compile time — @field enables reflection-driven access with no run-time lookup.