# Motivation [motivation]: #motivation This addresses a bunch of little things around `enum`s. None are individually that critical, but they *katamari*'d together in one area that made them seem big enough to write an RFC. ## Fix `repr(Rust)` on `enum`s We accidentally made it so that, unlike other types, `repr(Rust)` on an enum doesn't necessarily mean layout flexibility. That means that if you put `repr(Rust, u8)` on an `enum` -- say because you want to set the discriminants as `u8`s -- it can actually make it *bigger* than necessary, even if you didn't need a layout guarantee. Things with C-compatible layouts should be under something *other* than `repr(Rust)`, as that's much clearer in intent, and easier for people to remember: "repr Rust doesn't give layout guarantees". ## Give clearer names to C-compatible layouts Right now, `#[repr(C)]`, and `#[repr(u32)]`, and `#[repr(C, u32)]` all exist as C-compatible layouts. Do *you* remember which one is which layout? We should give them evocative names. ## Add a specific marker for categories that we already define elsewhere The conversation around [rust#116863] made me think about how we have special rules to allow `as` casting on enums, but there's no way to write something on your `enum` to make that intend explicit. [rust#116863]: https://github.com/rust-lang/rust/pull/116863 Like how we have `repr(transparent)` for structs, rather than just saying "well, so long as there's one field", we should have a similar explicit `repr` for `enum`s that don't want to ever have a payload other than the discriminant. ## Move the discriminant type out of being a `repr` issue into being "real" syntax Having the discriminant type in `repr` is somewhat unusual, because it affects even safe code that doesn't care about layout. Other `repr` things -- like `repr(C)` or `repr(transparent)` or `repr(packed)` -- are typically things that there's no semantic need to actually use if you're just writing pure safe code. But a change like ```diff -#[repr(u32)] pub enum Demo { A, B = u32::MAX, } ``` makes even the type definition stop compiling. Having it not in an attribute also would open the door to other things as well, like allowing type projections or more. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation ## `#[repr]`s for `enum`s The following are your choices of core `repr`s for enums. Each `enum` will be exactly one of the following: ### `#[repr(Rust)]` If you want to explicitly say you want layout optimizations (and that you don't need any layout promises), you can put `#[repr(Rust)]` on your `enum`. This is the default, so often won't be written out, but is useful in conversation to talk about a type that's not one of the other `repr`s. > **RFC Aside**: See below for how this won't always be completely true, but it's how I'd teach it and encourage people to think about it. ### `#[repr(transparent)]` If you have only one variant in your `enum`, you can put `#[repr(transparent)]` on the enum to have it do the same as a `struct` marked `#[repr(transparent)]`. > **RFC Aside**: No change here. ### `#[repr(fieldless)]` and `#[repr(C, fieldless)]` If you want an enum that's just its discriminant ("C-like"), then you can mark it `#[repr(fieldless)]`. That means that the layout and ABI of the enum will be exactly the same as that of its discriminant type, but all fields on variants need to be 1-ZSTs. Typically that means that all variants will be unit-like, but some nuanced situations might want to have `PhantomData` or similar in one or more of the variants. > **RFC Aside**: This is essentially the same category as "`enum`s that you can `as` cast", but we don't have a `repr` for that today. ### `#[repr(C, struct_with_union)]` This gives the "obvious" C-compatible layout for a discriminated union: a `struct` with the discriminant and a `union` of the variants. For example, this Rust type ```rust #[repr(C, struct_with_union)] enum MyEnum { A(f32, u64), B { x: u32, y: u8 }, } ``` has the same layout as the following: ```rust #[repr(C)] struct MyEnumRepr { tag: MyEnumDiscriminantType, payload: MyEnumPayload, } #[repr(C)] union MyEnumPayload { A: MyEnumPayloadA, B: MyEnumPayloadB, } #[repr(C)] struct MyEnumPayloadA(f32, u64); #[repr(C)] struct MyEnumPayloadB { x: u32, y: u8 } ``` > **RFC Aside**: This is exactly the same as `#[repr(C, Int)]` from [RFC#2195]. ### `#[repr(C, union_of_structs)]` This is a less-obvious translation to C, but one that can sometimes avoid extra alignment-caused padding: a `union` of `struct`s where all the structs have the tag in the same position. For example, this Rust type ```rust #[repr(C, union_of_structs)] enum MyEnum { A(f32, u64), B { x: u32, y: u8 }, } ``` has the same layout as the following: ```rust #[repr(C)] union MyEnumRepr { A: MyEnumPayloadA, B: MyEnumPayloadB, } #[repr(C)] struct MyEnumPayloadA(MyEnumDiscriminantType, f32, u64); #[repr(C)] struct MyEnumPayloadB { tag: MyEnumDiscriminantType, x: u32, y: u8 } ``` > **RFC Aside**: This is exactly the same as `#[repr(Int)]` from [RFC#2195]. [RFC#2195]: https://rust-lang.github.io/rfcs/2195-really-tagged-unions.html ## Discriminant types If you want to pick the discriminant type yourself, you can put a type in the enum definition: ```rust #[repr(fieldless)] enum Foo : pub windows::DWORD { Null = 0, LineFeed = 10, CarriageReturn = 13, } #[repr(C, struct_with_union)] enum Bar : i16 { Widget(*const c_void) = 123, File(u32) = 456, Error = -1, } ``` For now that type needs to not depend on generic parameters and resolve to an integer primitive (`uN` or `iN`). Those restrictions might be weakened in the future. `fieldless` types allow `as` casting to that specific discriminant type.