On this page
article
Chapter 11: Generics and Comptime
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});