String equality in comptime

Hi everyone,
I have a question about “string” comparison at compile time. I want to know the value of a specific structure’s field at compile time. For now, what I came up with is iterating over all the structure’s fields and stop when I find the one I want, something like:

const std = @import("std");

pub const Foo = struct { a: i64, bar: f64 };

pub fn main() void {
    const infos = @typeInfo(Foo);

    inline for (infos.@"struct".fields) |f| {
        if (std.mem.eql(u8, "a", f.name)) {
            @compileLog("Here");

            if ("a".len == f.name.len) {
                @compileLog("Same length");
            }
        }
    }
}

The call to eql evaluates as true for every field of the structure, resulting in printing twice "Here". When I look at std.mem.eql source code, I see that at compile time (after the if having for first condition !@inComptime()), the first test is comparing the length of the two slices.
I don’t know how but it seems to be true for each field’s name.

Source code of std.mem.eql:

pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
    if (!@inComptime() and @sizeOf(T) != 0 and std.meta.hasUniqueRepresentation(T) and
        eqlBytes_allowed)
    {
        return eqlBytes(sliceAsBytes(a), sliceAsBytes(b));
    }

    if (a.len != b.len) return false;
    ...
}

When I put the same length test in my code with the compile log "Same length", I see it printed only once in the console.

What I am missing here?

Output:

test_reflection.zig:10:13: error: found compile log statement
            @compileLog("Here");
            ^~~~~~~~~~~~~~~~~~~

Compile Log Output:
@as(*const [4:0]u8, "Here")
@as(*const [11:0]u8, "Same length")
@as(*const [4:0]u8, "Here")

Thanks!

That is weird indeed, I tried changing to a comptime block instead of inline for:

const std = @import("std");

pub const Foo = struct { a: i64, bar: f64 };

pub fn main() void {
    const infos = @typeInfo(Foo);

    comptime {
        for (infos.Struct.fields) |f| {
            if (std.mem.eql(u8, "a", f.name)) {
                @compileLog("Here");

                if ("a".len == f.name.len) {
                    @compileLog("Same length");
                }
            }
        }
    }
}

which works and only has 1 “Here” and “Same length” compile log, instead of 2 “Here” outputs.

[Edit: I tested on Zig 0.13.0, so the Struct vs @"struct" change]

1 Like

Zig would not evaluate functions at comptime without explisit comptime keyword. So this if turned into runtime check instead of been resolved at comptime.

With comptime compiling code would print

Compile Log Output:
@as(*const [4:0]u8, "Here")
@as(*const [11:0]u8, "Same length")
const std = @import("std");

pub const Foo = struct { a: i64, bar: f64 };

pub fn main() void {
    const infos = @typeInfo(Foo);

    inline for (infos.@"struct".fields) |f| {
        if (comptime std.mem.eql(u8, "a", f.name)) {
            // ^^^^^^^
            @compileLog("Here");

            if ("a".len == f.name.len) {
                @compileLog("Same length");
            }
        }
    }
}
3 Likes

Oh ok, I didn’t know that! Thanks, it works as expected :slight_smile: