# 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.