Rust: Should `enum Foo { SingleVariant }` be zero-size?

Created on 8 Nov 2016  路  8Comments  路  Source: rust-lang/rust

This prints 1 in rustc 1.14.0-nightly (cae6ab1c4 2016-11-05):

pub enum Foo { SingleVariant }
fn main() {
    println!("{}", ::std::mem::size_of::<Foo>());
}

I鈥檓 guessing this one byte is the enum鈥檚 discriminant. But since there鈥檚 a single variant, that discriminant is useless. Could such a type be zero-size? For what it鈥檚 worth, an empty enum Bar {} is already zero-size.

This type could be written struct Foo; which is zero-size, but a single-variant enum could come up if it鈥檚 generated by a macro that can generate any number of variants depending on its arguments.

Most helpful comment

@petrochenkov I think all of your example except the #[repr(C)] should be zero-size. I don鈥檛 think it鈥檚 unexpected that a repr attribute changes the memory representation.

All 8 comments

frewsxcv and misdreavus on IRC point out that the discriminant is already eliminated in some cases:

playbot: enum Foo { SingleVariant(i32) } ::std::mem::size_of::<Foo>()
4
playbot: enum Foo { SingleVariant(()) } ::std::mem::size_of::<Foo>()
0
playbot: enum Foo { SingleVariant{} } ::std::mem::size_of::<Foo>()
1

I'm quite sure it should be. This looks like a bug.

The thing is that an enum with 1 const-like variant counts as a "C-like" enum, and has a "semi-well-defined" representation. Not sure what to do about that.

I agree that this inconsistency feels like a bug.

@arielb1 The section https://doc.rust-lang.org/reference.html#enumerations of the Rust reference seems to only state that it can be cast (using as) to get the discriminant. It would be possible to preserve this behaviour even with a 0-sized type.

The change would possibly break assumptions of existing crates on the size of such enumerations (for example when they are transmuted). Would a crater run be sufficient to evaluate the impact?

@ranma42

Sure. The layout of repr(Rust) types is unspecified by design.

I agree that this inconsistency feels like a bug.

What about the size of

enum E {
    A = 10 // Enum behaves like an integer constant, it should probably be layed out as one
}

and then the size of

enum E {
    A = 0 // Should behave like previous one, it would be strange for layout to depend on concrete initialized value
}

and then

enum E {
    A // This is exact equivalent to the previous enum, why should it be layed out differently?
}

and what about this

#[repr(C)]
enum E {
    A // Hey, it looks so C-like I can probably transmute it to `c_int`!
}

I mean, whatever the behavior is, it would be inconsistent with something.
Now "C-like" takes priority over "univariant" and not otherwise. Seems okay, no strong reasons to change.

@petrochenkov I think all of your example except the #[repr(C)] should be zero-size. I don鈥檛 think it鈥檚 unexpected that a repr attribute changes the memory representation.

I agree with @SimonSapin here. There's also #[repr(u8)] and whatnot if you want to guarantee a specific size.

Duplicate of #15747.

Was this page helpful?
0 / 5 - 0 ratings