casting Rc<Self> of trait T to Rc<T> in rust

54 Views Asked by At

I have a trait which can be called T. In a lot of places i currently use Rc<dyn T>. The problem is that i wanted to add a default method to T that runs a function the needs Rc<dyn T>. But as far as i can see there is no way to cast Rc<Self> because Self is not sized in a trait. I can not make the whole project use generics, so how could i get around this?
example:

use std::rc::Rc;
trait T {
    fn run_test_method(self: Rc<Self>) {
        test_method(self);
    }
}

struct A;
impl T for A {}

fn test_method(_rc: Rc<dyn T>) {
    // do stuff with 'rc'
}
fn main() {
    let a = Rc::new(A) as Rc<dyn T>;
    a.run_test_method();
}
1

There are 1 best solutions below

2
cdhowie On BEST ANSWER

The compiler gives you multiple hints about this, and if you follow them all you will wind up with something like this, which does compile:

trait T {
    fn run_test_method(self: Rc<Self>)
    where
        Self: Sized + 'static,
    {
        test_method(self);
    }
}

The Sized bound is required so that the cast can be performed, and the 'static bound is required because there is no lifetime on dyn T in the test_method argument, which makes it implicitly dyn T + 'static.

If you can't require the 'static lifetime for some reason (because the implementors of T contain non-static borrows) then you can remove the 'static bound on T::run_test_method if you indicate that the dyn T can have a non-static lifetime on test_method:

fn test_method(_rc: Rc<dyn T + '_>) {
    // do stuff with 'rc'
}

If you want to be able to call this method with either an Rc<A> or an Rc<dyn T> then you will have to implement this separately for both cases, because fundamentally each method does something different. One casts a (pointer to a) sized value to a trait object, and the other does not.

You can accomplish this by using a helper trait that you implement for both cases. (The type names here will be a bit confusing to people who work in Rust regularly because T is usually a generic type, but here it's a trait.)

// Remove the method from this type.
trait T {}

trait RunTestMethod {
    fn run_test_method(self);
}

impl<X: T> RunTestMethod for Rc<X> {
    fn run_test_method(self) {
        test_method(self);
    }
}

impl RunTestMethod for Rc<dyn T + '_> {
    fn run_test_method(self) {
        test_method(self);
    }
}

Note the compiler allows these implementations because they don't overlap. Generic types are implicitly Sized unless otherwise specified, and dyn T is !Sized -- therefore the compiler can easily prove that the sets of types that each implementation will cover are disjoint.

Now, you can do this:

fn main() {
    let a = Rc::new(A);
    Rc::clone(&a).run_test_method();
    
    let b: Rc<dyn T> = a;
    b.run_test_method();
}

(Playground)