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:
trait Foo {}
impl<T> Foo for T {}
impl<'a, T> Foo for &'a mut T {}
This fails with the error
error[E0119]: conflicting implementations of trait `Foo` for type `&mut _`:
--> src/main.rs:3:1
|
2 | impl<T> Foo for T {}
| -------------------- first implementation here
3 | impl<'a, T> Foo for &'a mut T {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&mut _`
Strangely enough, this works:
trait Bar {}
impl<T> Bar for T
where
T: Clone,
{}
impl<'a, T> Bar for &'a mut T
where
T: Clone + 'static,
{}
Why does the version with the Clone
constraint work, and how can I make it work without it?
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.
The reason for the Clone
version works is because the types that the trait is being implemented for are no longer conflicting on the implementation.
Take the first example and add a default implementation.
trait Foo {
fn hi(&self){
println!("Hi");
}
}
And then we implement Foo
for all of type T
with impl<T> Foo for T {}
this actually implements enough for us to use a reference to our types and use the Foo
trait. For Example:
fn say_hi<'a>(b: &'a mut Foo){
b.hi();
}
fn main(){
let mut five = 5;
five.hi(); // integer using Foo
say_hi(&mut five); // &'a mut Foo
}
To answer the second part of you question, you didn't need the second implement of impl<'a,T> Foo for &'a mut T {}
because impl<T> Foo for T {}
was enough to give you what you were looking for.
Now that we have seen that the first example works without the second implement it starts to make sense that the example using Clone
works because you are implementing for a subset of types T
that are Clone
and a different subset of types &'a mut T
that are Clone+static
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With