# Reflection with @typeInfo and anytype — Zig

Source: https://www.geekswithgeeks.com/en/zig/k-reflection

> 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.

```zig
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.

**Quiz:** What does @field(value, name) do when name is comptime-known?

- [ ] Creates a new field at run time
- [ ] Deletes a field
- [x] 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.
