Generic field comparison at comptime

Hey y’all, what is the proper pattern for enforcing comptime guarantees for generic types? I’m a bit new to Zig so I’ve been reading the docs and playing around with the code, yet still haven’t gotten it working.

Contrived example:

fn Generic(comptime T: type, comptime a: i64) type {
    _ = T;
   return struct{
        const Self = @This();
        const b = a;

        fn new() Self {
            return .{};
        }

        fn testFn(self: Self, other: Self) void {
            if (self.b != other.b) {
                @compileError("bad match...");
            }
        }
    };
}

test "container comptime" {
    const G1 = Generic(f32, 1);
    const G2 = Generic(i64, 2);
    const g1 = G1.new();
    const g2 = G2.new();
    // I expect the error to get thrown here
    g1.testFn(g2);
}

The error makes sense;

src/main.zig:26:15 error: expected type 'main.Generic(f32,1)', found 'main.Generic(i64, 2)'

One thing to note is that even when modifying the test so that I am comparing the same type (e.g. ‘main.Generic(f32, 1)’), I recieve this error:

src/main.zig:14:22 error: no field named 'b' in struct 'main.Generic(f32, 1)'

Any help/guidance is appreciated!

The reason behind the second error is that b belongs to the type’s namespace, not its instance fields.

So, it’ll go away if you change the comparison to this:

if (@TypeOf(self).b != @TypeOf(other).b) {
    @compileError("bad match...");
}

Which would be the same as this:

if (Self.b != Self.b) {
    @compileError("bad match...");
}

Each time you call Generic a new type is generated.
To use different types in testFn you can use the anytype for the second argument.
As @tensorush posted but with anytype for other the testFn becomes:

        fn testFn(self: Self, other: anytype) void {
            _ = self;
            if (Self.b != @TypeOf(other).b) {
                @compileError("bad match...");
            }
        }
1 Like

Thanks! That all makes sense. What exactly forces a field into the type’s namespace versus being a field that is defined by the instance? Is it the “const”?

1 Like

Yes, in this case it is the const.

In the following struct, x and y are fields but the struct is a container and can have declarations such as const, var, fn, enum, union, struct, etc.

const Point = struct {
    x: f32,
    y: f32,
    const zero = Point{ .x = 0, .y = 0 };
};
2 Likes