Jaime López

Data Science Systems Developer

Compile time and the type-function pattern in Zig

Sep. 24, 2025

In Zig, instead of a collection of specialized language features, comptime is the engine for a type driven development approach. comptime allows the use of Zig regular code to generate, validate, or manipulate types at compile time.

The type-function is a basic pattern and illustrates the Zig's comptime feature. This kind of function returns a type that is based on the provided arguments. This is the way to implement generics in Zig.

The following example shows a Vector function that returns a struct containing an array which type and size attributes are defined based on the arguments passed to the function. It also defines the dot operation valid for the generated type.

pub fn Vector(comptime n: usize, comptime T: type) type {
    return struct {
        data: [n]T,
        pub fn dot(self: @This(), other: @This()) T {
            var sum: T = 0;
            for (0..n) |i| {
                sum += self.data[i] * other.data[i];
            }
            return sum;
        }
    };
}

When this function is called, it will be evaluated a compile time. For example, the following test creates a new type for a vector of three integers: Vec3. This is a generated type that can be used to create new values and perform operations among then. The test shows how two values are defined, the dot operation is applied and the result is confirmed.

test "Valid vector operations" {
    const Vec3 = Vector(3, i32);  // Defining a new type
    // Creating some data
    const A = Vec3{ .data = .{1, 2, 3} };
    const B = Vec3{ .data = .{4, 5, 6} };
    // Computing the dot product
    const y = A.dot(B);
    // Checking the result
    try std.testing.expect(y == 32);
}

The previous test will pass because is a correct program. However, the following one, which is trying to compute a dot product between a vector of three elements and another of two elements, will not be even compiled. It is an incorrect program because is mixing vectors of different size in the dot product: Vec3 and Vec2.

test "Vectors of different size" {                                               
    const Vec3 = Vector(3, f64);  // Vector of size 3                
    const Vec2 = Vector(2, f64);  // Vector of size 2                            
    const A = Vec3{ .data = .{1.0, 2.0, 3.0} };                                  
    const B = Vec2{ .data = .{4.0, 5.0} };                                       
    _ = A.dot(B);  // Invalid operation                            
}

This is the error reported by the Zig compiler:

vector.zig:29:15: error: expected type 'vector.Vector(3,f64)', found
'vector.Vector(2,f64)'
    _ = A.dot(B);
              ^
vector.zig:4:12: note: struct declared here (2 times)
    return struct {
           ^~~~~~
vector.zig:6:42: note: parameter type declared here
        pub fn dot(self: @This(), other: @This()) T {

In conclusion, comptime is a powerful feature in Zig that enables a type-driven development approach by allowing developers to generate, validate, or manipulate types at compile time using regular code.

The type-function pattern provides a fundamental mechanism for implementing generics in Zig, enabling the creation of correct and efficient data structures. With comptime, developers can write more expressive and concise code, leading to improved productivity and maintainability.