# Idea: Catch-all enum variant

**URL:** <https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523>\
**Category:** language design\
**Created:** [April 26, 2022, 6:21am UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523 "2022-04-26T06:21:09Z")\
**Posts on this page:** 19\
**Page:** 1

<div class="post-metadata">

**Author:** ![danielkeller](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/danielkeller/32/8614_2.png) [@danielkeller](https://internals.rust-lang.org/u/danielkeller)\
**Post date:** [April 26, 2022, 6:21am UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/1 "2022-04-26T06:21:09Z")

</div>

Anyone who's read a C enum in Rust has gone through the hassle of defining a "fake enum" as a wrapped integer type plus constants. What would make this much easier is the ability to define an enum variant whose representation is "any other bit pattern," for instance

```rust
#[repr(C, u32)]
enum ErrorCode {
    Success = 0;
    OutOfMemory = 1;
    FailedToFrobnicate = 2;
    Unknown = _;
}

```

So transmuting a `u32` to an `ErrorCode` would always be defined behavior. What do you think?

---

<div class="post-metadata">

**Author:** ![chrefr](https://avatars.discourse-cdn.com/v4/letter/c/e480ec/32.png) [@chrefr](https://internals.rust-lang.org/u/chrefr)\
**Post date:** [April 26, 2022, 6:40am UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/2 "2022-04-26T06:40:32Z")

</div>

Should it be able to carry payload?

---

<div class="post-metadata">

**Author:** ![hyeonu](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/hyeonu/32/2162_2.png) [@hyeonu](https://internals.rust-lang.org/u/hyeonu)\
**Post date:** [April 26, 2022, 8:01am UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/3 "2022-04-26T08:01:52Z")

</div>

> [@danielkeller](#):
>
> defining a "fake enum" as a wrapped integer type plus constants

If you want a wrapped integer type plus constants, why don't you define a wrapper type over integer with associated constants?

```rust
struct ErrorCode(u32);

#[allow(non_upper_case_globals)] // in case if you really want it
impl ErrorCode {
    const Success: Self = Self(0);
    const OutOfMemory: Self = Self(1);
    const FailedToFrobnicate: Self = Self(2);
}

fn get_error_code() -> ErrorCode {
    if some_cond() {
        ErrorCode::Success
    } else if another_cond() {
        ErrorCode::OutOfMemory
    } else {
        ErrorCode(ffi_get_error_code())
    }
}

fn handle_error_code() {
    match get_error_code() {
        ErrorCode::Success => {...}
        ErrorCode::OutOfMemory => {...}
        ErrorCode(unknown) => {...}
    }
}

```

---

<div class="post-metadata">

**Author:** ![chrefr](https://avatars.discourse-cdn.com/v4/letter/c/e480ec/32.png) [@chrefr](https://internals.rust-lang.org/u/chrefr)\
**Post date:** [April 26, 2022, 8:41am UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/4 "2022-04-26T08:41:43Z")

</div>

> [@hyeonu](#):
>
> If you want a wrapped integer type plus constants, why don't you define a wrapper type over integer with associated constants?
> 
> ```rust
> 
> ```

You lose exhaustiveness checking.

---

<div class="post-metadata">

**Author:** ![SadiinsoSnowfall](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/sadiinsosnowfall/32/8495_2.png) [@SadiinsoSnowfall](https://internals.rust-lang.org/u/SadiinsoSnowfall)\
**Post date:** [April 26, 2022, 10:12am UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/5 "2022-04-26T10:12:18Z")

</div>

This is also a problem I came accross not too long ago, I ended up using the "integer wrapper" method described by @hyeonu because I wanted the value of the `unknown` variant to be stable (as in not randomly changed by the compiler to a magic value when cast to an `ErrorCode` and cast back to an u32, which would not be the case when using an `enum` with a non-tagged variant if I'm not mistaken.

I managed to overcome the exhaustiveness checking by using the `const_assert_eq` macro from the [static\_assertions](https://crates.io/crates/static_assertions) crate to check the number of defined variants at compile time in every function that used the newtype. It's really not practical to do it like this but at least this gives you something resembling comptime exhaustiveness checking.

---

<div class="post-metadata">

**Author:** ![Nemo157](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nemo157/32/11585_2.png) [@Nemo157](https://internals.rust-lang.org/u/Nemo157)\
**Post date:** [April 26, 2022, 10:38am UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/6 "2022-04-26T10:38:24Z")

</div>

An enum with a catch-all variant also couldn't have true exhaustiveness checking (well, it's sort of possible, but really impractical, for a `repr(u8)` one).

What is the forward-compat story, if at some point in the future the enum has a new variant added to replace a value which was previously going to the `Unknown` variant? Either that is a breaking change (which it's not on the C side), or you have to use `#[non_exhaustive]` (and fully lose exhaustiveness checking, essentially ending up with something identical to using a wrapper type).

---

<div class="post-metadata">

**Author:** ![danielkeller](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/danielkeller/32/8614_2.png) [@danielkeller](https://internals.rust-lang.org/u/danielkeller)\
**Post date:** [April 26, 2022, 11:52am UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/7 "2022-04-26T11:52:25Z")

</div>

With an enum you also get the ability to have data-carrying variants.

---

<div class="post-metadata">

**Author:** ![ratmice](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ratmice/32/4917_2.png) [@ratmice](https://internals.rust-lang.org/u/ratmice)\
**Post date:** [April 26, 2022, 3:34pm UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/8 "2022-04-26T15:34:08Z")

</div>

The issue that I have with this is that the "any other bit pattern" implies that `Unknown` has a one-to-many relation as its repr value. As such which if any of these is `ErrorCode::Unknown as u32` supported to be. It is possible to enumerate and do `const` equality assertions on enums currently. If there is no single as representation I don't see how this would work.

I could perhaps see it being able to specify a range: e.g. `Reserved = ..` or `Reserved = 3..u32::MAX`, which I would find more sensible from a perspective of given proc\_macros a fighting chance to figure it out.

Edit: I should spell it out more clearly perhaps,

```rust
const fn foo() -> u32 { 0 }
#[repr(C, u32)]
enum Foo {
   Z = zero(),
  Unknown = _,
}

```

Presumably at some time in the future with a range expression a proc\_macro could generate some const code which iterates over the range of Unknown.

Edit2: This does not alas equivalent to the OP's proposal as it wouldn't allow disjoint ranges, you would have to include separate variants for each disjoint range. I don't find it a big deal, I cannot recall seeing a datasheet which included disjoint ranges into a single field at least..

---

<div class="post-metadata">

**Author:** ![hyeonu](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/hyeonu/32/2162_2.png) [@hyeonu](https://internals.rust-lang.org/u/hyeonu)\
**Post date:** [April 26, 2022, 3:43pm UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/9 "2022-04-26T15:43:57Z")

</div>

Um, do you want to transmute u32 to an enum type with data-carrying variants? Also such enum can't be written as a C enum.

If the enum is not for the FFI and doesn't have wrapped integer semantics, it would be better to have `Option<MyEnum>` where it can be missing than to add another variant represents missing value.

---

<div class="post-metadata">

**Author:** ![illicitonion](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/illicitonion/32/3886_2.png) [@illicitonion](https://internals.rust-lang.org/u/illicitonion)\
**Post date:** [April 27, 2022, 8:14am UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/10 "2022-04-27T08:14:04Z")

</div>

FWIW the [`num_enum`](https://crates.io/crates/num_enum) crate has supported something similar since the `arbitrary_enum_discriminant` feature landed in rust:

```rust
#[derive(FromPrimitive, IntoPrimitive)]
#[repr(u8)]
enum Enum {
    Zero = 0,
    #[num_enum(catch_all)]
    NonZero(u8),
}

```

It doesn't allow transmuting, but can implement `From` and `Into` both ways.

---

<div class="post-metadata">

**Author:** ![197g](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/197g/32/7276_2.png) [@197g](https://internals.rust-lang.org/u/197g)\
**Post date:** [April 27, 2022, 4:18pm UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/11 "2022-04-27T16:18:28Z")

</div>

Just a clarifying question: this does not influence the permissible representations, it only adds a conversion method that matches the variant by default, right? Using the crate it would _still_ be UB to dereference a pointer to a valid int as a pointer to the enum instead. This distinction is important for (mutable) references to data-carrying `enum` that can only be translated to a `union` in C-api bindings if it was valid instead.

I would very enjoy being able to write, with specific annotations if necessary, an enum that can correctly derived `bytemuck::Pod` and similar marker traits. And then parse network headers by casting without an extra copy to the stack.

---

<div class="post-metadata">

**Author:** ![bestouff](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bestouff/32/2595_2.png) [@bestouff](https://internals.rust-lang.org/u/bestouff)\
**Post date:** [April 28, 2022, 8:34am UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/12 "2022-04-28T08:34:59Z")

</div>

How do you check for equality when 2 different bit patterns fall into your `Unknown` variant ?

---

<div class="post-metadata">

**Author:** ![chrefr](https://avatars.discourse-cdn.com/v4/letter/c/e480ec/32.png) [@chrefr](https://internals.rust-lang.org/u/chrefr)\
**Post date:** [April 28, 2022, 9:47am UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/13 "2022-04-28T09:47:09Z")

</div>

`derive(PartialEq)` uses `std::mem::discriminant()`. So we'll just have to make [`intrinsics::discriminant_value()`](https://doc.rust-lang.org/stable/std/intrinsics/fn.discriminant_value.html) returning some stable value for this discriminant. It'll not be zero-cost, but it'll work.

---

<div class="post-metadata">

**Author:** ![H2CO3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/h2co3/32/2849_2.png) [@H2CO3](https://internals.rust-lang.org/u/H2CO3)\
**Post date:** [April 28, 2022, 10:20am UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/14 "2022-04-28T10:20:47Z")

</div>

The principal problem isn't that it's non-zero-cost. The much bigger issue is that it's very much non-obvious, and any "solution" will be counter-intuitive to a large amount of people who would expect a different (likely still reasonable in some way) default.

---

<div class="post-metadata">

**Author:** ![illicitonion](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/illicitonion/32/3886_2.png) [@illicitonion](https://internals.rust-lang.org/u/illicitonion)\
**Post date:** [April 28, 2022, 9:46pm UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/15 "2022-04-28T21:46:11Z")

</div>

Yes, exactly so - the representation doesn't change, so this is just for conversion (which may involve a stack copy), _not_ for transmuting or casting.

---

<div class="post-metadata">

**Author:** ![danielkeller](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/danielkeller/32/8614_2.png) [@danielkeller](https://internals.rust-lang.org/u/danielkeller)\
**Post date:** [April 29, 2022, 7:12am UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/16 "2022-04-29T07:12:35Z")

</div>

My intuition is that it should work like NaN and be unequal to everything including itself.

> [@H2CO3](#):
>
> The much bigger issue is that it's very much non-obvious

This is a very strong argument against the idea imo, since other people in this thread seem to have different intuitions

---

<div class="post-metadata">

**Author:** ![tcsc](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tcsc/32/3806_2.png) [@tcsc](https://internals.rust-lang.org/u/tcsc)\
**Post date:** [April 29, 2022, 7:18am UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/17 "2022-04-29T07:18:58Z")

</div>

> [@danielkeller](#):
>
> My intuition is that it should work like NaN and be unequal to everything including itself.

I'm a frequent float defender, but the weird float partial order, with NaNs not equal to themselves, is... honestly a mistake\[1\]. We should not replicate it here.

* * *

1. Note that I don't think _rust_ made a mistake in following them -- that was the right call, it's just the semantics which have time in again proven confusing and easy to forget.

---

<div class="post-metadata">

**Author:** ![RalfJung](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ralfjung/32/2415_2.png) [@RalfJung](https://internals.rust-lang.org/u/RalfJung)\
**Post date:** [April 30, 2022, 4:37pm UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/18 "2022-04-30T16:37:50Z")

</div>

> [@hyeonu](#):
>
> If you want a wrapped integer type plus constants, why don't you define a wrapper type over integer with associated constants?

That is indeed the right way to translate a C enum to Rust. In C, an enum is just an integer type with some named constants and can carry arbitrary values. In Rust, that is fundamentally _not_ the case (or else we would have to force you to always write a fall-back branch for each `match`). Rust using the name `enum` here is slightly unfortunate...

> So transmuting a `u32` to an `ErrorCode` would always be defined behavior.

This makes it not a Rust `enum` type any more, so IMO using `enum`s here is just using the wrong tool. We already have a tool to express what you are asking for, and that's a newtyped `u32`.

Maybe we should have nicer syntax for that? This can probably be done as a macro though.

> [@danielkeller](#):
>
> With an enum you also get the ability to have data-carrying variants.

C enums don't have that ability, either, so this cannot come up when translating a C enum to Rust.

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [July 29, 2022, 4:38pm UTC](https://internals.rust-lang.org/t/idea-catch-all-enum-variant/16523/19 "2022-07-29T16:38:07Z")

</div>

Closed on July 29, 2022, 4:38pm UTC

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
