Generic Functions

  fn largest[T: Eq](items: []const T): ?*const T {
    if items.len = 0 { return .None; }
    var best = &items[0];
    for item in items[1..] {
        if item.* > best.* { best := item; }
    }
    return best;
}

var numbers = [_]i32{ 3, 1, 4, 1, 5, 9, 2, 6 };
if largest(&numbers) |n| {
    debug.print("largest: {}\n", .{n.*});
}
  

Generic Types

  type Stack[T] = struct {
    items: List[T],
};

impl[T] Stack[T] {
    static fn new(): Self {
        return .{ .items = List[T]::new() };
    }

    fn push(*self, alloc: Allocator, val: T): !void {
        try self.items.push(alloc, val);
    }

    fn pop(*self): ?T {
        return self.items.pop();
    }

    fn peek(*const self): ?*const T {
        if self.items.len() = 0 { return .None; }
        return self.items.get(self.items.len() - 1);
    }

    fn isEmpty(*const self): bool {
        return self.items.isEmpty();
    }
}

provide Destroy for Stack[T] {
    type Args = Allocator;
    fn deinit(*self, alloc: Allocator) {
        self.items.deinit(alloc);
    }
}

var stack = Stack[i32]::new();
defer stack.deinit(alloc);

try stack.push(alloc, 1);
try stack.push(alloc, 2);
try stack.push(alloc, 3);

while stack.pop() -> val {
    debug.print("{}\n", .{val});
}
-- prints: 3, 2, 1
  

Comptime Functions

When you need to inspect type structure:

  fn printTypeInfo(comptime T: type) {
    const info = #typeInfo(T);
    debug.print("type: {}\n", .{#typeName(T)});
    debug.print("size: {} bytes\n", .{#sizeOf(T)});

    case info.repr {
        .Struct { fields } => {
            debug.print("struct with {} fields:\n", .{fields.len});
            inline for field in fields {
                debug.print("  .{}: {}\n",
                    .{field.name, #typeName(field.type.*)});
            }
        },
        .Enum { variants } => {
            debug.print("enum with {} variants\n", .{variants.len});
        },
        _ => debug.print("other type\n"),
    }
}

printTypeInfo(Point);
-- type: Point
-- size: 8 bytes
-- struct with 2 fields:
--   .x: f32
--   .y: f32
  

Comptime Generic Config Parsing

The Zig pattern — no macros, just comptime:

  fn parseConfig(comptime T: type, input: []const u8, alloc: Allocator): !T {
    const info = #typeInfo(T);
    case info.repr {
        .Struct { fields } => {
            var result: T = .{};
            inline for field in fields {
                const val = findValue(input, field.name) orelse continue;
                inline if field.type.* = []const u8 {
                    #fieldSet(result, field.name, val);
                } else inline if field.type.* = u16 {
                    #fieldSet(result, field.name, try parseU16(val));
                } else inline if field.type.* = bool {
                    #fieldSet(result, field.name, val = "true");
                } else {
                    #compileError("unsupported config field type: {}",
                        .{#typeName(field.type.*)});
                }
            }
            return result;
        },
        _ => #compileError("parseConfig requires a struct type"),
    }
}

type ServerConfig = struct {
    host:    []const u8 = "localhost",
    port:    u16        = 8080,
    verbose: bool       = false,
};

var cfg = try parseConfig(ServerConfig, config_text, alloc);
debug.print("{}:{}\n", .{cfg.host, cfg.port});