How to compare enum based on order instead of value?

Following code

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
#[repr(u8)]
pub enum Rank{
    Two = b'2',
    Three = b'3',
    Four = b'4',
    Five = b'5',
    Six = b'6',
    Seven = b'7',
    Eight = b'8',
    Nine = b'9',
    Ten,
    Jack = b'J',
    Queen = b'Q',
    King = b'K',
    Ace = b'A',
}


fn main() {

    let r1 = Rank::Jack;
    let r2 = Rank::Ace;
    
   if r1 < r2 {
       println!("{:?} rank is less than {:?}", r1, r2 );
   } else {
       println!("{:?} rank is greater than {:?}", r1, r2 );
   }
    
}

Above code prints

Jack rank is greater than Ace

This is coz, the ascill value of A is less than J. Is there a way to compare enum based on the order in the enum instead of value?

You would need to manually implement the Ord trait for your enum.

1 Like

can you please provide same sample or link that would help?

Pro tip: Make sure to get your playground links from the “Share” button, otherwise it’s a generic link to the playground’s home page.

Of course you can compare based on the order, but the default implementation will compare enum discriminants numerically, so you’d need to write it yourself.

For your code at hand, if the declaration order is the one you’re most interested in, I’m wondering why you want this ascii-as-a-byte kind of enum representation in the first place. Maybe you could provide more context as for how that’s used. If you want to associate the enum variants with string representations (perhaps also including the number Ten), then a simple function returning a &'static str migth be the most straightforward way.

4 Likes

Do you need Rank's discriminants to be those bytes? I would consider removing the = b'2' etc. assignments (after which PartialOrd will be derived based on the order the variants are defined) and instead adding some sort of get_symbol() -> Option<u8> method to Rank that uses a match to map variants to bytes.

2 Likes

As-is, the easiest way is probably to offer an inverse translation from your enum to its proper order, and compare based on that

impl Rank {
    fn enum_index(&self) -> u8 {
        match *self {
            Rank::Two => 0,
            Rank::Three => 1,
            Rank::Four => 2,
            Rank::Five => 3,
            Rank::Six => 4,
            Rank::Seven => 5,
            Rank::Eight => 6,
            Rank::Nine => 7,
            Rank::Ten => 8,
            Rank::Jack => 9,
            Rank::Queen => 10,
            Rank::King => 11,
            Rank::Ace => 12,
        }
    }
}
impl Ord for Rank {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.enum_index().cmp(&other.enum_index())
    }
}
impl PartialOrd for Rank {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(&other))
    }
}

Though if such a translation is necessary anyways, as noted above, I feel like doing the inverse and skipping on using a repr and = b'…' values instead seems nicer.

3 Likes

I started with assumption that I could use the ascii value to print. But, its of no use, especially considering 10.

Thanks, once discriminants are removed & after writing own to_sting function to print, its good now.

Thanks !!

An alternative to the function mapping enum variants to strings is just using a const array like:

const CARD_NAMES: [&str; 13] = [
    "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A",
];

then you can use an enum repr to index into that

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.