I have a situation in my codebase where i have one function which has a boxed trait object calling a function which expects an implementor of that trait, and i got the error message shown below. For some reason, Box does not implement Trait by default.
use std::any::Any;
trait SubTrait: DynClone {}
trait DynClone: Any {
fn dyn_clone(&self) -> Box<dyn DynClone>;
}
impl<T: Any + Clone + 'static> DynClone for T {
fn dyn_clone(&self) -> Box<dyn DynClone> {
Box::new(Clone::clone(self))
}
}
fn test_outer() {
test::<Box<dyn SubTrait>>();
}
fn test<L: SubTrait>() {}
error[E0277]: the trait bound `Box<(dyn SubTrait + 'static)>: SubTrait` is not satisfied
--> src\ecs\schedule.rs:124:9
|
124 | test::<Box<dyn SubTrait>>();
| ^^^^^^^^^^^^^^^^^ the trait `SubTrait` is not implemented for `Box<(dyn SubTrait + 'static)>`
|
I don't fully understand why that is, but i am more perplexed by a fix that happened to work
impl DynClone for Box<dyn SubTrait> {
fn dyn_clone(&self) -> Box<dyn DynClone> {
(self as &dyn DynClone).dyn_clone()
}
}
impl SubTrait for Box<dyn SubTrait> {}
I tried this fully expecting it to be rejected for a conflicting implementation but it was accepted for some reason.
Why are these implementations not conflicting?
impl DynClone for Box<dyn SubTrait> {
fn dyn_clone(&self) -> Box<dyn DynClone> {
(self as &dyn DynClone).dyn_clone()
}
}
impl<T: Any + Clone + 'static> DynClone for T {
fn dyn_clone(&self) -> Box<dyn DynClone> {
Box::new(Clone::clone(self))
}
}
I know its not because of the trait bounds on T because Rust does not consider trait bounds or where clauses when checking for conflicts.
I even verified that the trait bounds on T are not the fix with another type which is not clone, which leads to this error message.
use std::sync::mpsc::Receiver;
impl DynClone for Receiver<()> {
fn dyn_clone(&self) -> Box<dyn DynClone> {
Box::new(25_i32)
}
}
error[E0119]: conflicting implementations of trait `schedule::test::DynClone` for type `std::sync::mpsc::Receiver<()>`
--> src\ecs\schedule.rs:138:1
|
117 | impl<T: Any + Clone + 'static> DynClone for T {
| --------------------------------------------- first implementation here
...
138 | impl DynClone for Receiver<()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `std::sync::mpsc::Receiver<()>`
|
= note: upstream crates may add a new impl of trait `std::clone::Clone` for type `std::sync::mpsc::Receiver<()>` in future versions
Can someone explain what's happening here?
The constraints on T
are absolutely considered. These implementations don't conflict:
trait MyTrait {}
struct MyStruct; // not Clone
impl<T> MyTrait for T where T: Clone {}
impl MyTrait for MyStruct {}
You may be confused that the absence of a trait implementation on type cannot be relied upon unless the current crate owns the type or trait (see orphan rule). In the case above the negative inference is required that MyStruct: !Clone
, but that checks out because MyStruct
is local and doesn't implement Clone
. In your example with Receiver<()>
, neither Receiver
or Clone
are local, and thus the absence of that implementation can't be relied on.
The other nuance here is that Box
is (yet again) a special case. Normally, ForeignType<LocalType>
is not considered local, but (reference):
Note that for the purposes of coherence, fundamental types are special. The
T
inBox<T>
is not considered covered, andBox<LocalType>
is considered local.
So in your case Box<dyn SubTrait>
is known to not implement Clone
and is considered local, so it is known to not conflict with the blanket implementation.