---
# System prepended metadata

title: Early Partial Draft of an Enum Repr RFC

---

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