Search code examples
enumsrustmemory-layout

Why is this Rust enum not smaller?


Consider this silly enum:

enum Number {
    Rational {
        numerator: i32,
        denominator: std::num::NonZeroU32,
    },
    FixedPoint {
        whole: i16,
        fractional: u16,
    },
}

The data in the Rational variant takes up 8 bytes, and the data in the FixedPoint variant takes up 4 bytes. The Rational variant has a field which must be nonzero, so i would hope that the enum layout rules would use that as a discriminator, with zero indicating the presence of the FixedPoint variant.

However, this:

fn main() {
    println!("Number = {}", std::mem::size_of::<Number>(),);
}

Prints:

Number = 12

So, the enum gets space for an explicit discriminator, rather than exploiting the presence of the nonzero field.

Why isn't the compiler able to make this enum smaller?


Solution

  • Although simple cases like Option<&T> can be handled without reserving space for the tag, the layout calculator in rustc is still not clever enough to optimize the size of enums with multiple non-empty variants.

    This is issue #46213 on GitHub.

    The case you ask about is pretty clear-cut, but there are similar cases where an enum looks like it should be optimized, but in fact can't be because the optimization would preclude taking internal references; for example, see Why does Rust use two bytes to represent this enum when only one is necessary?