Implement trait for value and reference

#1

I’d like to implement a trait for some values (like MyType) and their references (&MyType). I tried to solve this by using the Borrow trait.

use std::borrow::Borrow;

trait MyTrait {}

impl<B> MyTrait for B where B: Borrow<bool> {}
impl<B> MyTrait for B where B: Borrow<usize> {}

fn main() {
}

This fails, because a struct might implement both traits. Is there a proper way to solve this or do I have to write each implementation for both variants?

0 Likes

#2

I just tried to solve this by implementing the trait for the reference variant and adding a generic trait implementation that should add the value variant for all existing reference variant implementations:

impl<'a> MyTrait for &'a bool
impl<'a, T> MyTrait for T where &'a T: MyTrait

But the compiler still says that they are conflicting and I don’t know why.

0 Likes

#3

Just out of curiosity, why would you want to express this using trait bounds? It’s usually done like this:

trait MyTrait {}

impl MyTrait for bool {}
impl<'a> MyTrait for &'a bool {}

Playground

0 Likes

#4

This is how my current implementation looks like. But since the implementation of bool and &bool are exactly the same, I wonder if I can somehow avoid to write two implementations for each type. The code gets quite messy when double-implementing the trait for about 20 types.

0 Likes

#5

Hmm, then maybe a macro?

trait MyTrait {}

macro_rules! impl_my_trait {
    ($($mty:ty),+) => {
        $(
            impl MyTrait for $mty {}
            impl<'a> MyTrait for &'a $mty {} 
        )+
    }
}

impl_my_trait!(bool, usize, i32);

Playground

0 Likes

#6

The implementation is different between different types. It’s only the same between Type and &Type. But yes, a macro would be an option. I just wanted to know if there is a less complex solution and why my attempt to solve this doesn’t work.

0 Likes

#7

Ok, in that case you can keep your blanket impl, and implement your trait for non-reference types.

impl MyTrait for bool
impl<'a, T> MyTrait for T where &'a T: MyTrait

That should work.

1 Like

#8
impl<'a, T> MyTrait for T where &'a T: MyTrait {}

Gives a

error[E0275]: overflow evaluating the requirement
  = note: required because of the requirements on the impl of `MyTrait` for `&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'a T`
  = note: required because of the requirements on the impl of `MyTrait` for `&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'a T`
  = note: required because of the requirements on the impl of `MyTrait` for `&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&'a T`
...
  = note: required because of the requirements on the impl of `MyTrait` for `&'a T`
note: required by `MyTrait`

But I don’t know why.

0 Likes

#9

I found a working solution:

use std::borrow::Borrow;

trait MyTrait {
    fn doit<T>(value: T) where T: Borrow<Self>;
}

impl MyTrait for bool {
    fn doit<T>(_value: T) where T: Borrow<Self> {}
}

impl MyTrait for usize {
    fn doit<T>(_value: T) where T: Borrow<Self> {}
}


fn main() {
    bool::doit(true);
    bool::doit(&true);
    usize::doit(1usize);
    usize::doit(&1usize);
}

The generic type parameter had to be moved from the type to the method declaration.

0 Likes