I\'m trying to create a trait and provide one implementation for all non-reference types, and another for all reference types.
This fails to compile:
As you have learned, a generic T
can be anything¹, so the Foo
impls overlap (conflict) whenever T
in the first impl is &'a mut U
, because the second impl also covers that case (when T
is U
).
The Clone
version works simply because &mut
references never implement Clone
, so there's no overlap between T where T: Clone
and &'a mut T
.² If you try to implement Bar
for immutable (&
) references, you will have a conflict again, because immutable references do implement Clone
.
[H]ow can I make it work without it?
If by "it" you mean one implementation for reference types and another, different one for non-reference types, that's not possible in Rust for the same reason you can't implement a trait one way for struct
s and another way for enum
s: there simply is no way to express it (in current Rust).
One common pattern that might work for you is implementing your trait individually for whatever non-reference types you need, and then adding a "blanket impl" that covers any reference to a type for which the trait is already implemented, e.g.:
impl Foo for u32 { ... }
impl Foo for i32 { ... }
impl<'a, T> Foo for &'a T where T: Foo + 'a { ... }
impl<'a, T> Foo for &'a mut T where T: Foo + 'a { ... }
¹ Well, anything that is Sized
, at least. You have to add ?Sized if that's not what you want.
² The where T: Clone + 'static
clause doesn't matter, because &'a mut T
will never be Clone
whether T
itself is or not.