Auto-implementing trait using a trait object

Often times I want to hash a custom struct based on some subset of its fields. It's tedious to implement Hash and PartialEq/Eq for all these structs separately, so I would like to create a separate trait Hashable, which my structs can implement to specify the fields to hash. Here's what I have:

pub trait Hashable {
    type HashKey: Hash + Eq;
    fn hash_key(&self) -> Self::HashKey;
}
impl<K: Hash + Eq> Hash for dyn Hashable<HashKey = K> {
    fn hash<H>(&self, state: &mut H)
    where
        H: Hasher,
    {
        self.hash_key().hash(state);
    }
}
impl<K: Hash + Eq> PartialEq for dyn Hashable<HashKey = K> {
    fn eq(&self, other: &Self) -> bool {
        self.hash_key() == other.hash_key()
    }
}
impl<K: Hash + Eq> Eq for dyn Hashable<HashKey = K> {}

Then I have a concrete struct:

struct MyStruct {
    id: u32
}
impl Hashable for MyStruct {
    type HashKey = u32;
    fn hash_key(&self) -> Self::HashKey {
        self.id
    }
}

However, when I try to use MyStruct, it says that it does not implement Hash. What am I doing wrong, and what I can do to achieve what I want? Thanks in advance.

dyn Hashable<…> is a distinct type from any struct that implements Hashable, so implementing traits for it won’t implement those same traits for the struct. What you really want is generics, but Rust’s orphan rules prevent it from working:

use core::hash::Hasher;
use core::hash::Hash;

pub trait Hashable {
    type HashKey: Hash + Eq;
    fn hash_key(&self) -> Self::HashKey;
}

impl<T, K: Hash + Eq> Hash for T where T: Hashable<HashKey = K> {
    fn hash<H>(&self, state: &mut H)
    where
        H: Hasher,
    {
        self.hash_key().hash(state);
    }
}
impl<T, K: Hash + Eq> PartialEq for T where T: Hashable<HashKey = K> {
    fn eq(&self, other: &Self) -> bool {
        self.hash_key() == other.hash_key()
    }
}
impl<T, K: Hash + Eq> Eq for T where T: Hashable<HashKey = K> {}

   Compiling playground v0.0.1 (/playground)
error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
 --> src/lib.rs:9:6
  |
9 | impl<T, K: Hash + Eq> Hash for T where T: Hashable<HashKey = K> {
  |      ^ type parameter `T` must be used as the type parameter for some local type
  |
  = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
  = note: only traits defined in the current crate can be implemented for a type parameter

error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
  --> src/lib.rs:17:6
   |
17 | impl<T, K: Hash + Eq> PartialEq for T where T: Hashable<HashKey = K> {
   |      ^ type parameter `T` must be used as the type parameter for some local type
   |
   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
   = note: only traits defined in the current crate can be implemented for a type parameter

error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
  --> src/lib.rs:22:6
   |
22 | impl<T, K: Hash + Eq> Eq for T where T: Hashable<HashKey = K> {}
   |      ^ type parameter `T` must be used as the type parameter for some local type
   |
   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
   = note: only traits defined in the current crate can be implemented for a type parameter

For more information about this error, try `rustc --explain E0210`.
error: could not compile `playground` due to 3 previous errors

Sorry for not providing a solution; I’m short on time right now

Thanks. That makes sense. Yes, I was looking to avoid generics because of the orphan rule.

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.