Keep two 7 bit structs in a byte wide enum

48 Views Asked by At

I have two structs, which both only need 7 bits to represent themselves:

#[repr(transparent)]
struct TypeFlags(u8);

// TypeFlags has capabilities that are invalid for Control
impl std::ops::BitOr for TypeFlags {
    type Output = Self;

    fn bitor(self, rhs: Self) -> Self::Output {
        Self(self.0 | rhs.0)
    }
}

#[repr(transparent)]
struct Control(u8);

Since both types only need 7 bits, an "Either" type could be created which only occupies one byte.

// problem: the compiler sets aside
// a "flag" byte for the enum variant.
// size = 2
enum Either {
    Type(TypeFlags),
    Ctrl(Control)
}

// problem: pattern matching is painful
struct PackedEither(u8);

Are there any sub-byte optimizations for enum that I can take advantage of, or will I have to manually implement the bit flag on a struct like PackedEither?

1

There are 1 best solutions below

0
cafce25 On BEST ANSWER

The compiler is not able to make that optimization, but for the sake of argument let's assume the compiler were to make it and lets say TypeFlags gets stored as 0b0xxx_xxxx and Control as 0b1xxx_xxx.

Now here is the problem:
In Rust we can match on a reference to Either and get references to the contained values:

fn unwrap_control_ref(either: &Either) -> &Control {
    let Either::Ctrl(control) = either else {
        panic!("was not a `Either::Ctrl`");
    };
    control
}

Now the compiler would have to produce code, that references a value of Control but, it does not store that anywhere in memory it only stores a "Control | 0b1000_0000".

Sadly even if we were to use standard library internal attributes to programatically change the range of TypeFlags to 0..128 and Control to 128..256, it still does not use the nieche like we want and optimize to a single byte.