Because of the rules of making a safe trait object, the following code doesn't compile:
struct A<T> {
n: T
}
trait B {
fn get<T>(&self, a: &A<T>) where Self: Sized {
println!("aaaa");
}
}
struct C;
impl B for C {}
let c: Box<dyn B> = Box::new(C);
c.get(&a)
Error: the `get` method cannot be invoked on a trait object
╭─[command_289:1:1]
│
1 │ c.get(&a)
· ─┬─
· ╰─── error: the `get` method cannot be invoked on a trait object
───
the code get<T>(&self, a: &A<T>) has type parameters, which break the rule.
So is there a way I can make a trait object to get to the the target of above?
One simple option which sometimes works well enough is to "hoist" the type parameter up onto the trait instead of having it on the method.
trait Hoisted<T> {
fn get(&self, a: &A<T>);
}
impl<T, U> Hoisted<T> for U
where
U: B,
{
fn get(&self, a: &A<T>) {
B::get(self, a)
}
}
fn check() {
let a = A { n: () };
let c: Box<dyn Hoisted<()>> = Box::new(C);
c.get(&a);
}
This prevents you from easily using the same trait object with multiple types filled in for the type parameter though, so it doesn't always work.
Another option is to use type erasure in some way.
trait Erased {
fn get(&self, a: &A<Box<dyn Any>>);
}
impl<T> Erased for T
where
T: B,
{
fn get(&self, a: &A<Box<dyn Any>>) {
B::get(self, a)
}
}
fn check() {
let a: A<Box<dyn Any>> = A { n: Box::new(()) };
let c: Box<dyn Erased> = Box::new(C);
c.get(&a)
}
Here I'm just forcing A to have a trait object inside which probably isn't how you would set things up in real code, but it was the simplest way to make things work without modifying your original definition of B.