Zig NEWS

Govind
Govind

Posted on

Understanding comptime var

This is what Zig documentation says:

In Zig, the programmer can label variables as comptime. This guarantees to the compiler that every load and store of the variable is performed at compile-time. Any violation of this results in a compile error.

And this is in the section about local variables:

A local variable may be qualified with the comptime keyword. This causes the variable's value to be comptime-known, and all loads and stores of the variable to happen during semantic analysis of the program, rather than at runtime. All variables declared in a comptime expression are implicitly comptime variables.

What does this mean ? The documentation is confusing, so let's break it down:

  1. comptime vars are local (can be defined only within functions, comptime or @cimport blocks)
  2. Every load (read) of this variable and every store (write) to this variable happens at compile time.

Now it isn't clear to me what the restriction on read is (someone please explain in comments if you do), given that reads do not change the value of the variable and it should be safe to to read this value during compile time AND runtime.

What is interesting is the statement about writes.

comptime vars can be written to (as it is a var), but unlike regular vars, the compiler should be able to know ALL the values that this variable can take during the course of the program, otherwise it will refuse to compile.

Here is an unrealistic example, that will nevertheless illustrate how it works.

const std = @import("std");
const RndGen = std.rand.DefaultPrng;

fn set_a_comptime(a: u32) u32 {
    comptime var p: u32 = undefined;
    if (a < 33) {
        p = 44;
    } else {
        p = 97;
    }
    return p;
}

pub fn main() void {
    var rnd = RndGen.init(0);
    var some_random_num = rnd.random().int(u32);
    _ = set_a_comptime(some_random_num);
}
Enter fullscreen mode Exit fullscreen mode

When this program runs, the local variable p can have only 2 values: 44 or 97. This is known at compile time and therefore this program is compiled

Instead of setting p to 44 or 97, can we set p to a ?

comptime var p: u32 = undefined;
    if (a < 33) {
        p = a;
    } else {
        p = 97;
    }
Enter fullscreen mode Exit fullscreen mode

We get the following error message

./learn_comptime.zig:8:13: error: cannot store runtime value in compile time variable
        p = a;
Enter fullscreen mode Exit fullscreen mode

We do not know what value can possibly be in a as it is random, so the compiler refuses to store p to the value of a.

Where can this be used ? Ideally something like this (not possible now), would be a great usecase:

const all_commands = .{ "doom", "duke nukem 3d", "quake", "elder scrolls" };

fn set_a_comptime(a: u32) u32 {
    comptime var p: u32 = undefined;
    p = a;
    return all_commands[p].len;
}   

pub fn main() void {
    var rnd = RndGen.init(0);
    var some_random_num = rnd.random().intRangeAtMost(u32, 0, all_commands.len);
    _ = set_a_comptime(some_random_num);
}
Enter fullscreen mode Exit fullscreen mode

To us, it is clear that our random value is between 0 and the length of our all_commands array and therefore can be used to access an index within it, but the Zig compiler cannot know this (at the current state of the compiler).

We can nevertheless use comptime vars to acccess indexes within arrays/slices if there is a guarantee that it is < length(array), as shown in the Zig documentation

fn performFn(comptime prefix_char: u8, start_value: i32) i32 {
    var result: i32 = start_value;
    comptime var i = 0;
    inline while (i < cmd_fns.len) : (i += 1) {
        if (cmd_fns[i].name[0] == prefix_char) {
            result = cmd_fns[i].func(result);
        }
    }
    return result;
}
Enter fullscreen mode Exit fullscreen mode

Discussion (1)

Collapse
kristoff profile image
Loris Cro

Now it isn't clear to me what the restriction on read is (someone please explain in comments if you do), given that reads do not change the value of the variable and it should be safe to to read this value during compile time AND runtime.

I think the intended meaning is that during comptime the variable can be mutated and referring to it means that you will read from it its current value (which might change during as the comptime evaluation progresses). At runtime the value of the variable is fixed as it has been finalized during comptime, so all references to it are implicitly replaced with the value of the variable.

In other words, a comptime var variable is const at runtime.