Search code examples
zig

why do user defined types in zig need to be const?


In case I need to declare a struct in Zig I have to prefix it with a const

const Arith = struct {
    x: i32,
    y: i32,

    fn add(self: *Arith) i32 {
        return self.x + self.y;
    }
};

test "struct test" {
    var testArith = Arith{
        .x = 9,
        .y = 9,
    };

    expect(testArith.add() == 18);
}

But it can be initialized both ways as var and const so why does the type declaration need a constant keyword when it only matters whether the instance of the struct is const or not?


Solution

  • Need to be const because the order of evaluation in the root scope is undefined and because the variables of type type only can live in the compiler (has no memory representation, the compiler is unable to produce a binary representation of it). But you can use var inside other scopes:

    comptime {
       var T = struct { value: u64 };
       
       const x: T = .{ .value = 3 };
    
       T = struct { ok: bool };
    
       const y: T = .{ .ok = true };
    
       @compileLog(x.value); // <- 3
       @compileLog(y.ok); // <- true
    }
    

    Run this code

    In the rest of the answer, I explain in detail.

    Const

    const Arith = struct {...};
    

    Creates a constant variable of a inferred type. In this case, the variable Arith has type type:

    const Arith = struct {...};
    comptime {
       @compileLog(@TypeOf(Arith)); // <- type
    }
    

    Run this code


    This is the same as declare the variable as:

    const Arith: type = struct {...};
    

    Var

    You also can create a variable with var

    Examples:

    comptime {
       var Arith = struct {...};
    }
    
    comptime {
       var Arith: type = struct {...};
    }
    
    fn main() !void {
       comptime var Arith = struct {...};
    }
    
    fn main() !void {
       var Arith: type = struct {...};
    }
    
    fn main() !void {
       comptime var Arith: type = struct {...};
    }
    

    Because is a variable you can modify it:

    comptime {
       var T = u64;
       T = bool;
       @compileLog(T); // <-- bool
    }
    
    

    Run this code


    Comptime Types

    There is types that can only live in the compiler, like: type or structs that have a field of type anytype or other comptime type.

    In the case of type, this make the compiler interpret var x: type as comptime var x: type.

    Then, consider the following code:

    var T = struct { value: u64 }; // <- Compiler error
    
    comptime {
       const x: T = .{ .value = 3 };
    }
    
    error: variable of type 'type' must be constant
    

    because the order of evaluation in the root scope is undefined, the compiler forces to create a global variable inside the binary, but the type type has no memory representation. So, the compiler raises an error.