Skip to content

What `comptime` Means

A Zig program runs in two stages.

What comptime Means

A Zig program runs in two stages.

First, the compiler runs.

Then the program runs.

Most languages keep these stages mostly separate. Zig does not. Zig allows parts of a program to execute while the compiler is still compiling the program. This is called compile-time execution.

The keyword is:

comptime

Compile-time execution is one of the central ideas in Zig. It replaces much of what other languages solve with preprocessors, templates, macros, code generators, or special metaprogramming systems.

The simplest example uses a value known during compilation.

const std = @import("std");

pub fn main() void {
    const x = comptime fibonacci(10);
    std.debug.print("{d}\n", .{x});
}

fn fibonacci(n: u32) u32 {
    if (n < 2)
        return n;

    return fibonacci(n - 1) + fibonacci(n - 2);
}

The output is:

55

The important point is not the result. The important point is when the work happens.

fibonacci(10) runs while the compiler is compiling the program. The executable already contains the value 55.

The generated machine code does not need to compute Fibonacci numbers at runtime.

A compile-time value must be known completely during compilation.

This is valid:

const n = comptime 4 * 8;

This is not:

var x: u32 = 10;
const y = comptime x * 2;

The compiler cannot evaluate x * 2 at compile time because x is a runtime variable.

Compile-time execution uses the same language as runtime execution. There is no second metaprogramming language.

Functions are ordinary functions:

fn square(x: u32) u32 {
    return x * x;
}

They can run at runtime:

const a = square(5);

Or at compile time:

const b = comptime square(5);

The language does not split these into separate systems.

This becomes more useful when types are involved.

In Zig, types are values known at compile time.

A function can receive a type as an argument:

const std = @import("std");

fn printType(comptime T: type) void {
    std.debug.print("{s}\n", .{@typeName(T)});
}

pub fn main() void {
    printType(i32);
    printType(f64);
}

The output is:

i32
f64

The parameter:

comptime T: type

means:

  • T is known during compilation
  • T itself is a type value

This is the basis of generic programming in Zig.

Compile-time execution can also control declarations.

const std = @import("std");

pub fn main() void {
    comptime {
        std.debug.print("during compilation\n", .{});
    }

    std.debug.print("during execution\n", .{});
}

The first message appears while the compiler is compiling the program. The second appears when the executable runs.

Compile-time blocks are often used for validation.

comptime {
    if (@sizeOf(usize) < 8) {
        @compileError("64-bit system required");
    }
}

If the condition fails, compilation stops immediately.

Compile-time execution is not a textual substitution system. It is not like the C preprocessor.

The compiler executes Zig code with full type checking and semantic analysis.

This matters because generated code remains ordinary Zig code. Errors are usually easier to understand than macro expansion errors in other systems.

A common use of comptime is generating specialized code.

fn add(comptime T: type, a: T, b: T) T {
    return a + b;
}

The calls:

add(i32, 3, 4)
add(f64, 1.5, 2.5)

produce versions specialized for different types.

The compiler knows the exact type in each case.

Compile-time execution is powerful, but it should stay simple.

Use it when:

  • types must be selected
  • declarations must be generated
  • validation should happen during compilation
  • runtime overhead should disappear

Do not use it merely because it exists.

Most ordinary logic should still run at runtime.

Exercise 10-1. Write a function cube and evaluate it with comptime.

Exercise 10-2. Write a function that receives comptime T: type and prints the type name.

Exercise 10-3. Use a comptime block to reject systems where @sizeOf(usize) is smaller than 8.

Exercise 10-4. Write a generic max function using a compile-time type parameter.