Best way to implement common functionality between for structs

So, I have a large number of structs that share the same functionality, and I am wondering what is the best way to handle this in rust? I'm coming from a primarily C++ (and related) background with OOP.

Specifically, assume we have something like this:

pub trait Component{
foo();
bar();
}
pub struct XXXComponent{
id: u128
}

Each XXXComponent must implement PartialEq and Hash but specifically based on the id field. What I am doing currently is just hard coding all of them, but since I am repeating the exact same lines of code (and I have a large number of these components), I have a feeling there should be a more intelligent (and succinct) way to write this.

Sure. You can use derive macro to do so. Just like this:

#[derive(PartialEq,Hash)]
pub struct XXXComponent{
    id: u128
}

EDIT: Sorry I didn't see we implement them only based on id field. So the answer below may be the best solution.

I'm not sure it's the best way, but given that you can't implement traits on other traits without dyn, you could use a macro, and if you use this enough you could just make your own derive(CustomHashable) proc macro:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=3952e31ebc0894898ed064d817bcbd2b

use std::collections::HashSet;

macro_rules! custom_hashable {
    ($t:ident) => {
        impl std::hash::Hash for $t {
            fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
                self.id.hash(state)
            }
        }
        impl std::cmp::PartialEq for $t {
            fn eq(&self, other: &Self) -> bool {
                self.id == other.id
            }
        }
        impl std::cmp::Eq for $t {}
    }
}

struct Thing1 {
    id: u128,
    another: u32
}
custom_hashable!{ Thing1 }

struct Thing2 {
    id: u128,
    another: u32
}
custom_hashable!{ Thing2 }


fn main() {
    let item1 = Thing1 { id: 0, another: 0 };
    let item2 = Thing1 { id: 0, another: 1 };
    
    let mut set = HashSet::new();
    println!("Not already present? {:?}", set.insert(item1));
    println!("Not already present? {:?}", set.insert(item2));
}
2 Likes

Thank you all! I think implementing a custom derive macro is probably the best way of doing something like this. Now I just have to learn how rust macros bloody work!

I've been working though the Rust for Rustaceans book and just hit the section macros which is very good! (And the only reason it was front of mind to answer your question)