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 Options (where None should come first). As there were only two variants it was trivial to work around.
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 u32s.
(On mobile right now, or else Iād type an example)
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.
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.
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.