4

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?

Boiethios
  • 38,438
  • 19
  • 134
  • 183
Tom Anderson
  • 46,189
  • 17
  • 92
  • 133
  • 3
    This has been discussed before. I guess the answer is the compiler isn't smart enough (yet) – Denys Séguret Aug 02 '19 at 12:46
  • @DenysSéguret: The layout would be that `Rational::numerator` and `FixedPoint` overlap, and `Rational::denominator` does not overlap with anything. If the memory occupied at `Rational::denominator` is 0, then it's a `FixedPoint`, and otherwise it's a `Rational`. – Matthieu M. Aug 02 '19 at 12:49
  • 1
    related : https://stackoverflow.com/questions/16504643/what-is-the-overhead-of-rusts-option-type – Denys Séguret Aug 02 '19 at 12:52
  • This is [issue #46213](https://github.com/rust-lang/rust/issues/46213). I took a stab at implementing it myself a couple years back but did not succeed. However, things have changed in rustc in the meantime in such a way that I think it may be easier now. – trent Aug 02 '19 at 12:55

1 Answers1

4

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?

trent
  • 25,033
  • 7
  • 51
  • 90