Is it possible to create a macro that implements Ord by delegating to a struct member?

Viewed 488

I have a struct:

struct Student {
    first_name: String,
    last_name: String,
}

I want to create a Vec<Student> that can be sorted by last_name. I need to implement Ord, PartialOrd and PartialEq:

use std::cmp::Ordering;

impl Ord for Student {
    fn cmp(&self, other: &Student) -> Ordering {
        self.last_name.cmp(&other.last_name)
    }
}

impl PartialOrd for Student {
    fn partial_cmp(&self, other: &Student) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for Student {
    fn eq(&self, other: &Student) -> bool {
        self.last_name == other.last_name
    }
}

This can be quite monotonous and repetitive if you have a lot of structs with an obvious field to sort by. Is it possible to create a macro to automatically implement this?

Something like:

impl_ord!(Student, Student.last_name)

I found Automatically implement traits of enclosed type for Rust newtypes (tuple structs with one field), but it's not quite what I'm looking for.

2 Answers

I created a macro which allows implementing Ord by defining expression which will be used to compare elements: ord_by_key::ord_eq_by_key_selector, similar to what you were asking.

use ord_by_key::ord_eq_by_key_selector;

#[ord_eq_by_key_selector(|s| &s.last_name)]
struct Student {
    first_name: String,
    last_name: String,
}

If you have to sort by different criteria in different cases, you can introduce a containers for your struct which would implement different sorting strategies:

use ord_by_key::ord_eq_by_key_selector;

struct Student {
    first_name: String,
    last_name: String,
}

#[ord_eq_by_key_selector(|(s)| &s.first_name)]
struct StudentByFirstName(Student);

#[ord_eq_by_key_selector(|(s)| &s.last_name, &s.first_name)]
struct StudentByLastNameAndFirstName(Student);
Related