A function parameter can be marked comptime.
fn repeat(comptime n: usize, value: u8) [n]u8 {
var out: [n]u8 = undefined;
for (&out) |*item| {
item.* = value;
}
return out;
}Here n is not an ordinary parameter. It must be known while the program is being compiled.
This call is valid:
const a = repeat(4, 'x');The compiler sees n == 4, so the return type becomes:
[4]u8The array length is part of the type. The compiler cannot create [n]u8 unless it knows n.
This call is not valid:
var len: usize = 4;
const b = repeat(len, 'x');len is a runtime variable. Its value may change while the program runs. It cannot be used where the compiler needs a type.
A compile-time parameter lets a function receive information that shapes the program itself.
Types are the most common example.
fn zero(comptime T: type) T {
return 0;
}The call:
const a = zero(i32);
const b = zero(u64);creates values of different types from the same function.
The first call returns i32. The second returns u64.
A generic function in Zig is just an ordinary function with compile-time parameters.
fn add(comptime T: type, a: T, b: T) T {
return a + b;
}Use it like this:
const x = add(i32, 10, 20);
const y = add(f64, 1.25, 2.75);The compiler checks each use separately.
For i32, + means integer addition.
For f64, + means floating-point addition.
If a type does not support the operation, compilation fails.
const bad = add(bool, true, false);This is rejected because + is not defined for bool.
There is no separate interface declaration here. The function body itself states what it needs from T.
If the function uses +, then T must support +.
This style keeps generic code close to ordinary code.
Compile-time parameters may also be booleans.
fn log(comptime enabled: bool, message: []const u8) void {
if (enabled) {
std.debug.print("{s}\n", .{message});
}
}When enabled is known at compile time, the compiler can remove the branch when it is false.
log(false, "not printed");This call need not generate any printing code.
The choice was made before the program ran.
Compile-time parameters may also control data layout.
fn Buffer(comptime N: usize) type {
return struct {
data: [N]u8,
len: usize,
fn empty() Buffer(N) {
return .{
.data = undefined,
.len = 0,
};
}
};
}This function returns a type.
const Small = Buffer(16);
const Large = Buffer(4096);Small and Large are different types.
Small contains [16]u8.
Large contains [4096]u8.
The function Buffer is not a factory for runtime objects. It is a function that builds a type during compilation.
This is a common Zig pattern.
const std = @import("std");
fn Buffer(comptime N: usize) type {
return struct {
data: [N]u8,
len: usize,
fn init() Buffer(N) {
return .{
.data = undefined,
.len = 0,
};
}
fn append(self: *Buffer(N), byte: u8) !void {
if (self.len >= N)
return error.Full;
self.data[self.len] = byte;
self.len += 1;
}
};
}
pub fn main() !void {
var b = Buffer(4).init();
try b.append('a');
try b.append('b');
std.debug.print("{s}\n", .{b.data[0..b.len]});
}The output is:
abThe size of the buffer is known at compile time. No heap allocation is needed.
Compile-time parameters should be used when they change types, layout, or generated code.
They should not be used for ordinary values merely because those values happen to be constants.
This is good:
fn Matrix(comptime rows: usize, comptime cols: usize) type {
return struct {
data: [rows][cols]f64,
};
}The dimensions are part of the type.
This is usually unnecessary:
fn printNumber(comptime n: i32) void {
std.debug.print("{d}\n", .{n});
}The number does not shape the program. A normal parameter is enough.
Compile-time parameters are part of the function’s type-level interface. They tell the reader which values must be known before execution starts.
Exercise 10-5. Write a function arrayOf that takes comptime n: usize and a value, and returns an array of length n.
Exercise 10-6. Write a generic function min with a comptime T: type parameter.
Exercise 10-7. Write a function Pair(comptime T: type) type that returns a struct with fields first: T and second: T.
Exercise 10-8. Write Stack(comptime T: type, comptime N: usize) type using an array of length N.