Comptime Generics
Generic Programming via Comptime
Zig does not have a separate generics syntax like C++ templates or Java generics. Instead, it uses comptime type parameters to achieve the same result --- and more, since you can run arbitrary code at compile time to generate types and functions.
Generic Functions
A generic function accepts a comptime T: type parameter and uses T in its signature and body:
fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}
// Usage:
const m = max(i32, 10, 20); // 20
const n = max(f64, 3.14, 2.71); // 3.14
The compiler generates a specialized version for each type used. There is no runtime overhead.
Generic Data Structures
Functions that return type are Zig's way of defining generic structs. The returned struct is a completely normal type:
fn Stack(comptime T: type) type {
return struct {
items: [256]T = undefined,
len: usize = 0,
const Self = @This();
pub fn push(self: *Self, value: T) void {
self.items[self.len] = value;
self.len += 1;
}
pub fn pop(self: *Self) T {
self.len -= 1;
return self.items[self.len];
}
};
}
var stack = Stack(i32){};
stack.push(42);
const val = stack.pop(); // 42
@TypeOf and Type Reflection
@TypeOf(expr) returns the compile-time type of any expression. Combined with @typeInfo, you can inspect types and branch on their properties:
fn isInteger(comptime T: type) bool {
return switch (@typeInfo(T)) {
.int, .comptime_int => true,
else => false,
};
}
@TypeOf is particularly useful for writing helper functions that adapt to their input:
fn double(value: anytype) @TypeOf(value) {
return value * 2;
}
const a = double(@as(i32, 5)); // i32: 10
const b = double(@as(f64, 2.5)); // f64: 5.0
anytype Parameters
The anytype keyword tells the compiler to infer the type from the call site. It is syntactic sugar that avoids an explicit comptime T: type parameter when you only need one inferred type:
fn debugPrint(value: anytype) void {
const T = @TypeOf(value);
if (@typeInfo(T) == .int) {
std.debug.print("int: {}\n", .{value});
} else {
std.debug.print("other\n", .{});
}
}
Your Task
-
Write a generic function
fn Pair(comptime T: type) typethat returns a struct with two fields:first: Tandsecond: T, and a methodpub fn sum(self: @This()) Tthat returnsself.first + self.second. -
Write a function
fn typeName(comptime T: type) []const u8that returns"integer"ifTis an integer type,"float"if it is a floating-point type, or"other"otherwise. Use@typeInfoto inspect the type.