Collapsing up to eight `Option` discriminants into one byte

Suppose I have the struct

struct S {
	x: Option<u8>,
	y: Option<u8>,
	z: u8,
}

Naively this struct contains 5 bytes of data: x and y both need a one-byte discriminant + a one-byte payload, and z is one byte. This is confirmed by std::mem::size_of::<S>().

But, since each Option only uses one bit of its discriminant, you could actually save a byte by storing x's and y’s discriminant bits in the same byte. You could store up to eight Option discriminants in the same byte in this manner. One could consider this an advanced niche optimization: an Option’s discriminant byte has a niche that another Option can make use of. Is there any way to enable this optimization?

The representation you propose is incompatible with being able to create references such as &mut s.x. In principle, it could exist — #[repr(packed)] already prohibits references — but there is nothing like it in the language now.

10 Likes

Since you'd have to give up on getting a &Option<u8> anyway, you could rewrite your enum to represent the possible cases directly.

enum S {
    Xyz { x: u8, y: u8, z: u8 },
    Xz { x: u8, z: u8 },
    Yz { y: u8, z: u8 },
    Z(u8),
}

Side nit: default layouts are unspecified, not guaranteed to be optimal in any particular way, and not guaranteed to not change.[1] So you can't necessarily draw solid conclusions from the size today.


  1. Example. ↩ī¸Ž

6 Likes

If the bytes only need to represent up to 255 distinct values, rather than 256, then you can make use of NonZero<T> through Option<NonZero<u8>> which does allow taking references. If the value you don't need is 56, you can just wrapping sub or xor with 56 before storing in the NonZero<u8> and do the reverse upon retrieval.

4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.