Compile-Time Type Generation: A Generics Alternative
Introduction
Zig doesn’t have dedicated generic syntax. Instead, it achieves generic programming through compile-time type generation.
Because code can execute during compilation, functions can build and return entirely new types. This form of meta-programming makes Zig extremely flexible while keeping the language simple.
Example: Custom Type Generator
MyGenerator takes a compile-time type “T” and returns a new struct type containing a single field named “data”.
fn MyGenerator(comptime T: type) type {
return struct {
data: T,
};
}NOTE
Tmust be known at compile time. In fact any comptime variable must be known at compile time.
Usage: Creating Type Constants
const myu8data: type = MyGenerator(u8); // struct { data: u8 }
const myi8data: type = MyGenerator(i8); // struct { data: i8 }Creating simple type aliases is useful, but compile-time expression allow much more dynamic type generation.
pub fn List(comptime T: type, comptime is_view: bool) type {
const Datatype = if (is_view) []const T else []T;
const AllocatorType = if (is_view) void else std.mem.Allocator;
return struct {
allocator: AllocatorType,
data: Datatype,
len: usize,
pub fn init(
allocator: AllocatorType,
data: Datatype,
) @This() {
return @This(){
.allocator = allocator,
.data = data,
.len = data.len,
};
}
};
}
const ls = List(u8, true);
// struct {
// allocator: void,
// data: []const u8,
// len: usize,
// }NOTE
DatatypeandAllocatorTypeare computed at compile time because they depend only on compile-time parameters.
Compile-time execution is not limited to selecting types. It can also compute values used to define a type.
pub fn StaticTensor(comptime T: type, comptime dim: []const usize) type {
comptime {
if (dim.len == 0) {
@compileError("Static dim cannot be 0");
}
}
const size = comptime block: {
var result: usize = 1;
for (dim) |value| {
result *= value;
}
break :block result;
};
return struct {
data: [size]T,
len: usize,
};
}NOTE
A function returning
typeacts as a type factory. Each unique set of compile-time arguments produces a distinct concrete type.