Rust: `sort_by` multiple criteria, verbose pattern matching

I have a Vector of players to be sorted by the following criteria:

  1. by championships (descending)
  2. by wins (descending)
  3. by name (ascending)

I implemented it as follows in Rust:

use std::cmp::Ordering;

#[derive(Debug)]
struct Player {
    name: String,
    championships: u8,
    wins: u8,
}

fn main() {
    let mut players = vec![
        Player {
            name: "Alice".to_string(),
            championships: 3,
            wins: 17,
        },
        Player {
            name: "Bob".to_string(),
            championships: 3,
            wins: 19,
        },
        Player {
            name: "Claire".to_string(),
            championships: 4,
            wins: 18,
        },
        Player {
            name: "Dan".to_string(),
            championships: 4,
            wins: 18,
        },
    ];
    players.sort_by(|a, b| match b.championships.cmp(&a.championships) {
        Ordering::Less => Ordering::Less,
        Ordering::Greater => Ordering::Greater,
        Ordering::Equal => match b.wins.cmp(&a.wins) {
            Ordering::Less => Ordering::Less,
            Ordering::Greater => Ordering::Greater,
            Ordering::Equal => match a.name.cmp(&b.name) {
                Ordering::Less => Ordering::Less,
                Ordering::Greater => Ordering::Greater,
                Ordering::Equal => Ordering::Equal,
            },
        },
    });
    dbg!(players);
}

This works as intended, as the output shows:

[src/main.rs:46] players = [
    Player {
        name: "Claire",
        championships: 4,
        wins: 18,
    },
    Player {
        name: "Dan",
        championships: 4,
        wins: 18,
    },
    Player {
        name: "Bob",
        championships: 3,
        wins: 19,
    },
    Player {
        name: "Alice",
        championships: 3,
        wins: 17,
    },
]

However, the pattern matching code is very repetitive, because in most cases I just return what I’ve already got, e.g.:

Ordering::Less => Ordering::Less,
Ordering::Greater => Ordering::Greater,

Is there any shortcut to avoid this kind of verbosity?

It’s not really a sorting problem but a pattern matching problem. (Maybe the sorting by multiple criteria could be done in a more elegant way, which I didn’t figure out how to do, given that I have ascending/descending criteria to sort by.)

>Solution :

Chaining comparisons as in your example is what std::cmp::Ordering::then (and then_with) is for:

players.sort_by(|a, b| {
    b.championships
        .cmp(&a.championships)
        .then(b.wins.cmp(&a.wins))
        .then(b.name.cmp(&a.name).reverse())
});

Leave a Reply