When does `Option<MyEnum>` have the same size as `MyEnum`?

I am making a board game and have the following enum.

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Stone {
    Red,
    Green,
    Blue,
    Yellow,
}

I wanted to see what the size of Option<Stone> and Stone were, so I ran the following code and found that both had a size of 1 on Rust 1.80.1.

fn main() {
    // Both print 1.
    println!("{}", std::mem::size_of::<Option<Stone>>());
    println!("{}", std::mem::size_of::<Stone>());
}

I know that Option<NonZero<T>> and T have the same size, but I don't know what happens when NonZero is not used in the definition, as in the Stone example above.

Why are Option<Stone> and Stone the same size?

Every non-zero-sized type is at least 1 byte, so if you use at most 255 enum variants, there'll always be niche for the None variant. If you transmute your Option<Stone> to a u8, you can see that values 0, 1, 2, 3 are used as the discriminant, and 4 is used for None.

10 Likes

Here's the documentation of the guaranteed cases.

The optimization isn't guaranteed to happen in other cases (such as with your enum), but still may happen.

6 Likes