An interesting finding

Function as method and non-method are treated differently.

const T = struct {
   pub fn foo(t: T) void {
       _ = t;
       return;
   }
};

pub fn main() !void {
	var x: T = .{};
	
	// The following two lines are not treated equally.
	x.foo(); // without the line, the code fails to compile.
	T.foo(x);
}
1 Like

I’m not a Zig expert, but looks like since x.foo() may be rewritten to T.foo(&x) if the function takes a pointer, it is considered as a possible mutation because the check is done at an early stage of the compilation, before even looking at the definition of foo.

3 Likes

Commenting out x.foo();

deleteme.zig:9:9: error: local variable is never mutated
    var x: T = .{};
        ^
deleteme.zig:9:9: note: consider using 'const'

This is because the unmodified variable check is performed on the AST, before semantic analysis. The signature of foo could be fn foo(t: *T) void, which would require x to be a variable.

6 Likes