Get last value of enum

I need to get the maximum value of an enum at compile time. In C++ I can do this:

enum class MyEnum {
  a = 1,
  b = 2,
  c = 3,
  
  LAST = c,
};

Then I can create an array to store some stuff for each enum value, like this:

int countOfEachType[(int)MyEnum::LAST];

But how do I do this in Zig? I tried this:

const MyEnum = enum(u32) {
    a = 0,
    b = 1,
    c = 2,

    LAST = c,
};

but it says “use of undeclared identifier ‘c’”
I tried this:

const MyEnum = enum(u32) {
    a = 0,
    b = 1,
    c = 2,

    LAST = .c,
};

but it says “type ‘u32’ has no members”
So then I gave up and tried this:

const MyEnum = enum(u32) {
    a = 0,
    b = 1,
    c = 2,

    LAST = 2,
};

But it says “enum tag value 2 already taken”

Or is there a more Zig-ish way to get the max enum value? Something like @maxEnumValue(MyEnum)?

From the language reference on enums

const Small = enum {
    one,
    two,
    three,
    four,
};

// @typeInfo tells us the field count and the fields names:
test "@typeInfo" {
    try expect(@typeInfo(Small).@"enum".fields.len == 4);
    try expect(mem.eql(u8, @typeInfo(Small).@"enum".fields[1].name, "two"));
}

Edit:

If you give explicit integer values to your enum, you would have to access the last element of fields, and get its value.

There is also std.enums.EnumArray, which provides you with an array, that can be indexed by your enum:

If you want to do the manual approach, you can just define a declaration like so:

const MyEnum = enum(u32) {
    a = 0,
    b = 1,
    c = 2,

    pub const last: MyEnum = .c;
};

// Usage:
// const maximum: MyEnum = .last;

If you want a more general solution, @swenninger’s strategy is close, but isn’t quite complete. The order of the fields provided by @typeInfo is based on the order that they are declared in the file, not their values. So, you would need to check the value of every field and get the highest one:

pub fn greatestEnumTag(comptime Enum: type) Enum {
    return comptime blk: {
        const enum_info = @typeInfo(Enum).@"enum";
        if (enum_info.fields.len == 0) {
            @compileError("enum has no fields");
        }

        var max_value: enum_info.tag_type = enum_info.fields[0].value;
        var max_name: []const u8 = enum_info.fields[0].name;

        for (enum_info.fields[1..]) |field| {
            if (field.value > max_value) {
                max_value = field.value;
                max_name = field.name;
            }
        }

        break :blk @field(Enum, max_name);
    };
}

EDIT: The above code can be simpler:

pub fn greatestEnumTag(comptime Enum: type) Enum {
    return comptime blk: {
        const enum_info = @typeInfo(Enum).@"enum";
        if (enum_info.fields.len == 0) {
            @compileError("enum has no fields");
        }

        var max_value: enum_info.tag_type = enum_info.fields[0].value;

        for (enum_info.fields[1..]) |field| {
            max_value = @max(max_value, field.value);
        }

        break :blk @enumFromInt(max_value);
    };
}
4 Likes