Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Compile-time evaluation

comptime marks work that happens while the program is being compiled, not while it runs.

comptime blocks

A comptime block runs during compilation.

fn main() -> i32 {
    comptime {
        let size : i32 = 4 * 4;
    }
    return 0;
}

Everything inside has to be knowable at compile time. A comptime block cannot read a run-time value, call into C, or touch a device.

comptime conditions

if comptime chooses a branch at compile time. The branch not taken is not compiled.

fn main() -> i32 {
    if comptime 1 < 2 {
        return 0;
    }
    return 1;
}

This is different from an ordinary if with a constant condition. An ordinary if is compiled in full and then possibly folded by the optimiser; if comptime decides before code generation, so the untaken branch does not have to compile at all. That matters when a branch is only valid for some types or some hardware.

Const generic parameters

A generic parameter can be a value rather than a type, written with const:

fn buffer_size<const N : i32>() -> i32 {
    return N;
}

fn main() -> i32 {
    return buffer_size<4>() - 4;
}

The value is fixed when the function is instantiated, so it can be used where a compile-time constant is required — most usefully in a tensor’s shape.

Where this is used

Compile-time evaluation is what lets a shape be part of a type. Tensor<f32, [4, 4]> needs 4 to be known while type checking, not while running, and const generic parameters are how a function can be generic over a shape without giving up that knowledge:

fn main() -> i32 {
    let t : Tensor<f32, [2, 2]> = Tensor<f32, [2, 2]>();
    return 0;
}

Because the shape is in the type, a matrix multiply whose dimensions do not line up is a compile error rather than a run-time crash — the same argument as contracts, applied to dimensions.

Where to next