Implement Trait for another Trait

Greetings!

Any way to implement trait for another trait? I have HashMap where keys are some id from object, but these id store inside objects and I can use HashSet to get rid of duplicates. But problem is to implement PartialEq / Hash for generic type:

trait Foo {
    fn dbid(&self) -> u32;
}

impl PartialEq for dyn Foo {
    fn eq(&self, other: &Self) -> bool {
        self.dbid() == other.dbid()
    }
}
impl Eq for dyn Foo {}

impl Hash for dyn Foo {
    fn hash<H: Hasher>(&self, state: &mut H) {
        state.write_u32(self.dbid());
    }
}

impl Borrow<u32> for dyn Foo {
    fn borrow(&self) -> &u32 {
        &self.dbid()
    }
}

#[derive(Debug)]
struct Object {
    dbid: u32,
    name: SmolStr,
    password: SmolStr,
}

impl Foo for Object {
    fn dbid(&self) -> u32 {
        self.dbid
    }
}

fn main() {
    let mut set = HashSet::new();
    set.insert(Object {
        dbid: 1,
        name: SmolStr::new_inline("name1"),
        password: SmolStr::new_inline("password1"),
    });

    println!("{:?}", set.get(&1u32));
}
/Users/asinotov/.cargo/bin/cargo build --color=always --message-format=json-diagnostic-rendered-ansi
   Compiling playground v0.1.0 (/Users/asinotov/CLionProjects/playground)
error[E0277]: the trait bound `Object: Eq` is not satisfied
   --> src/main.rs:46:16
    |
46  |       set.insert(Object {
    |  _________------_^
    | |         |
    | |         required by a bound introduced by this call
47  | |         dbid: 1,
48  | |         name: SmolStr::new_inline("name1"),
49  | |         password: SmolStr::new_inline("password1"),
50  | |     });
    | |_____^ the trait `Eq` is not implemented for `Object`
    |
note: required by a bound in `HashSet::<T, S>::insert`
   --> /Users/asinotov/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/std/src/collections/hash/set.rs:422:8
    |
422 |     T: Eq + Hash,
    |        ^^ required by this bound in `HashSet::<T, S>::insert`

error[E0277]: the trait bound `Object: Hash` is not satisfied
   --> src/main.rs:46:16
    |
46  |       set.insert(Object {
    |  _________------_^
    | |         |
    | |         required by a bound introduced by this call
47  | |         dbid: 1,
48  | |         name: SmolStr::new_inline("name1"),
49  | |         password: SmolStr::new_inline("password1"),
50  | |     });
    | |_____^ the trait `Hash` is not implemented for `Object`
    |
note: required by a bound in `HashSet::<T, S>::insert`
   --> /Users/asinotov/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/std/src/collections/hash/set.rs:422:13
    |
422 |     T: Eq + Hash,
    |             ^^^^ required by this bound in `HashSet::<T, S>::insert`

error[E0599]: the method `get` exists for struct `AHashSet<Object>`, but its trait bounds were not satisfied
  --> src/main.rs:52:26
   |
32 | struct Object {
   | -------------
   | |
   | doesn't satisfy `Object: Eq`
   | doesn't satisfy `Object: Hash`
...
52 |     println!("{:?}", set.get(&1u32));
   |                          ^^^ method cannot be called on `AHashSet<Object>` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `Object: Eq`
           `Object: Hash`
help: consider annotating `Object` with `#[derive(Eq, Hash, PartialEq)]`
   |
32 | #[derive(Eq, Hash, PartialEq)]
   |

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0277, E0599.
For more information about an error, try `rustc --explain E0277`.
error: could not compile `playground` due to 4 previous errors
Process finished with exit code 101

None at all. This is by design though as traits are not types.

You can, however, make PartialEq et al supertraits of Foo:

trait Foo: PartialEq {
    fn dbid(&self) -> u32;
}

In the case of PartiqlEq, this guarantees that any value of any type T implementing Foo is comparable to any other value of type T.

Note that this might not be object safe (I'm currently traveling so I can't practically test it out), meaning you can't use it as dyn Foo.

No.
You can however, do something like: trait Foo: PartialEq + Eq. This would ensure that whenever Foo is implemented, PartialEq and Eq must be implemented.

1 Like

Note that dyn Trait is a distinct, singular, concrete type. It does not represent all implementing types per se; that is, implementations on dyn Trait directly are not applicable to non-type-erased types, even if they implement the Trait.

(There are some exceptional behaviours for dyn Trait that I personally hope get removed.)

2 Likes

You also seem to confuse implementing a trait for a type and requiring a type to implement a trait. Implmenting a trait means that you provide some behavior, and you can only do this if you own either the type or the trait (i.e., they are part of your own crate). You have to decide which one you are trying to do.

1 Like