A problem about unsafe trait object

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.

Playground with the full code


Oh also just to be clear "object safe" traits have nothing to do with unsafe code or traits. You can't construct an "object unsafe" trait object

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.