Packed struct as enum tag

Quick and easy: Looking for reasons/explanations, why following syntax doesn’t work (aside “not being currently supported by compiler”)

const DirectionProps = packed struct(u2) {
    vertical: bool,
    reversed: bool,
};

const Direction = enum(DirectionProps) {
    left_to_right = .{ .vertical = false, .reversed = false }, // 0b00
    right_to_left = .{ .vertical = false, .reversed = true }, // 0b01
    top_to_bottom = .{ .vertical = true, .reversed = false }, // 0b10
    bottom_to_top = .{ .vertical = true, .reversed = true }, // 0b11
};

Since enums only accept integers as tags, why not packed structs? Since they:

  1. Must have define backing integer (as of more recent versions of compiler)
  2. Are basically a bit by bit representation of said integer
  3. Supports many integer operations without having to create “helper” functions

Not only that, but @intFromEnum returns backing integer.
Why not something similar, that returns backing struct?
I know I can still @bitcast it directly to said struct (and it works), but I need to specify resulting type.

I managed to get something like this working like this:

const Direction = enum(@typeInfo(DirectionProps).@"struct".backing_integer.?) {
    left_to_right = @bitCast(DirectionProps{ .vertical = false, .reversed = false }),
    top_to_bottom = @bitCast(DirectionProps{ .vertical = true, .reversed = false }),
    right_to_left = @bitCast(DirectionProps{ .vertical = false, .reversed = true }),
    bottom_to_top = @bitCast(DirectionProps{ .vertical = true, .reversed = true }),
};

and I’m getting values by:

const dir: Direction = .left_to_right;
const props: DirectionProps = @bitCast(dir);
_ = props.vertical;
_ = props.reversed;

which is a bit tedious to write, but it works.

2 Likes

I think for this your example the following would work better

const Direction  = packed struct(u2) {
    vertical: bool,
    reversed: bool,

    const left_to_right: Direction = .{ .vertical = false, .reversed = false }; // 0b00
    const right_to_left: Direction = .{ .vertical = false, .reversed = true }; // 0b01
    const top_to_bottom: Direction = .{ .vertical = true, .reversed = false }; // 0b10
    const bottom_to_top: Direction = .{ .vertical = true, .reversed = true }; // 0b11
};
3 Likes

Cool, but then you can’t switch upon a direction like you could if it was an enum.
EDIT - this is wrong… see below…

Or use it in a tagged union.

yes you can since 0.16

Sorry, I meant you can’t

switch (direction) {
   left_to_right => ...

(or .{ .left_to_right... , as it might be)

Yes, since 0.16, you could indeed

switch (direction) {
   .{ .vertical = 1 } => ...
}

and the like.

Unless I’m missing something.

This works for me:

test "switch" {
    const dir: Direction = .left_to_right;

    const x = switch (dir) {
        .left_to_right => 1,
        .right_to_left => 2,
        .top_to_bottom => 3,
        .bottom_to_top => 4,
    };

    std.debug.print("x: {d}\n", .{x});
}

The power of decl literals

Ahhh! I see. I confess I didn’t try it, and (obviously) didn’t realize this would work. Clever.

EDIT: there are some threads from the past several months with similar enum-ish frustrations that might benefit from this technique. Hopefully those folks see this thread.

1 Like