How to sort enum variants?

I'd like to implement `PartialOrd` for an enum where the inner value shall not be taken into account:

``````#[derive(PartialOrd, Ord, PartialEq, Eq)]
enum E {
First(u32),
// potentially some more variants
Last(u32),
}
``````

This doesn't work because the inner `u32` will be recursively evaluated due to the `derive`. `std::mem::discriminant` doesn't help because it's not `[Partial]Ord`.

I peeked into the macro expansion to see how the `derive` was implemented. I found `core::intrinsics::discriminant_value` which is unavailable to normal code.

I know two workarounds:

• implement a private `my_discriminant(&self) -> u8` and hard-code the discriminants
• implement `[partial_]ord()` with an exhaustive `match (self, other) { ā¦ }`

I had a similar problem before, trying to find the minimum of two `Option`s (where `None` should come first). As there were only two variants it was trivial to work around.

Is there a nicer solution?

It doesn't seem to me like the derive evaluates the inner `u32`. The following runs without panicking:

``````#[derive(PartialOrd, Ord, PartialEq, Eq)]
enum E {
First(u32),
// potentially some more variants
Last(u32),
}

fn main() {
assert!(E::First(50) < E::Last(24));
assert!(E::First(24) < E::Last(50));
}
``````

When `derive` d on enums, variants are ordered by their top-to-bottom discriminant order.

They're taken into account as second criterion.

This will pass but shouldn't. (if the inner value was ignored they should be equal)

See my comment (sorry there was a typo) - I don't think they can be compared using `ord`. Or did I miss something?

You could define a wrapper type `Unordered<T>` that always returns `None` from `PartialOrd::cmp`, and then store `Unordered<u32>`s in the enum in place of the undecorated `u32`s.

(On mobile right now, or else Iād type an example)

Yes, that would be another workaround. Ergonomics would suffer, though.

I was able to find this closed issue that talks about implementing `Ord` for `mem::Discriminant`: https://github.com/rust-lang/rust/issues/51561
Scottmcm brought up a good point against doing so, but the result seems to be that we don't currently have an easy and ergonomic way* to compare discriminants in stable rust.

*The boilerplate would probably be easy to do with proc macros though. But bringing in proc macros just for this is less than ideal.

Thank you for the reference!

This might be the conclusion:

this seems rare enough that it's not worth landing in std.

The odd thing is that enums can be ordered (using `derive`) - you can just not stop that from taking the inner types into account.

You could follow that suggestion (wrapping in `Unordered<T>`) on a private, otherwise unused version of your struct, and then implement the traits yourself in terms of the other struct.

I think you mean the enums.
Regardless, that requires making sure that the other enum is always up to date with the real enum. But if you're gonna copy the variants into the other enum by hand, at that point you might as well write
the `my_discriminant(&self) -> u8` instead.

1 Like

Yeah, that's true, and silly for another reason to boot. If mirroring the enum was the direction taken, just leaving out the payloads altogether would have the same effect. I've used a crate that could do the mirroring (creates a new enum with the same names but no payloads), but I don't know that it's really worth it here.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.