Clone and Cast Rc pointer

2019-08-18 16:06发布

this is kind of a follow up question from this: Rust dynamic cast trait object between different taits

The solution provided there works really well when we use references for trait objects.

This time I was trying to do the same with Rc pointers. For example

  • I have a super trait named TraitAB and 2 traits named TraitA and TraitB
  • So when I first create a trait object of type TraitAB instead of using a Box, now I use an Rc pointer.
  • I need a variable of type TraitA to be a reference of ab

Here I made a very minimal example:

use std::rc::Rc;

trait TraitAB : TraitA + TraitB {
    fn as_a(&self) -> Rc<dyn TraitA>;
    fn as_b(&self) -> Rc<dyn TraitB>;
}

trait TraitA {}
trait TraitB {}

struct MyType {}

impl TraitAB for MyType {
    fn as_a(&self) -> Rc<dyn TraitA> {Rc::clone(self)}
    fn as_b(&self) -> Rc<dyn TraitB> {Rc::clone(self)}
}

impl TraitA for MyType {}
impl TraitB for MyType {}

fn main() {
    let a: Rc<dyn TraitA>;
    let b: Rc<dyn TraitB>;
    {
        let mut ab: Rc<dyn TraitAB> = Rc::new(MyType{});
        a = ab.as_a();
        b = ab.as_b();
    }
}

This doesn't work though. According to the error messages:

xx |     fn as_a(&self) -> Rc<dyn TraitA> {Rc::clone(self)}
   |                                                 ^^^^ expected struct `std::rc::Rc`, found struct `MyType`
   |
   = note: expected type `&std::rc::Rc<dyn TraitA>`
              found type `&MyType`

methods as_a and as_b can't know self is actually an Rc pointer. Is there a way to do the cast of a cloned shared pointer?

Thanks

2条回答
虎瘦雄心在
2楼-- · 2019-08-18 16:38

You need to implement TraitAB on RC<MyType>

Here is the code :

use std::rc::Rc;

trait TraitAB {
    fn as_a(&self) -> Rc<dyn TraitA>;
    fn as_b(&self) -> Rc<dyn TraitB>;
}

trait TraitA {}
trait TraitB {}

struct MyType {}

impl TraitAB for Rc<MyType> {
    fn as_a(&self) -> Rc<dyn TraitA> {
        Rc::clone(self) as Rc<dyn TraitA>
    }
    fn as_b(&self) -> Rc<dyn TraitB> {
        Rc::clone(self) as Rc<dyn TraitB>
    }
}

impl TraitA for MyType {}
impl TraitB for MyType {}

fn main() {
    let a: Rc<dyn TraitA>;
    let b: Rc<dyn TraitB>;
    {
        let mut ab:&TraitAB = &Rc::new(MyType{});
        a = ab.as_a();
        b = ab.as_b();
    }
}

Btw i don't see any reason for TraitAB to extend TraitA + TraitB but you can extend and implement TraitA and TraitB for Rc<MyType> as well.

You can find working example with the implemented functions for TraitA and TraitB in here : Playground

查看更多
虎瘦雄心在
3楼-- · 2019-08-18 16:47

methods as_a and as_b can't know self is actually an Rc pointer.

Actually, that's not true! There's a rarely used feature that allows self to be taken as various standard kinds of references (Rc<Self>, Box<Self>, etc.).

That means that you can rewrite your TraitAB as

trait TraitAB : TraitA + TraitB {
    fn as_a(self: Rc<Self>) -> Rc<dyn TraitA>;
    fn as_b(self: Rc<Self>) -> Rc<dyn TraitB>;
}

Unfortunately, as written, as_a and as_b move self: Rc<Self>, since Rc<T> doesn't implement Copy (only Clone). One way to fix this is to simply clone ab before passing it into these methods. This also means that you don't need to clone the self inside the method. (playground link)

let ab: Rc<dyn TraitAB> = Rc::new(MyType{});
let _a: Rc<dyn TraitA> = ab.clone().as_a();
let _b: Rc<dyn TraitB> = ab.clone().as_b();

Using the nightly-only feature arbitrary_self_types, it's possible to make as_a and as_b take self as &Rc<Self> (which looks weird to me since it's a reference to a reference). This allows ab.as_a() to be called without moving ab. The only problem with this approach is that TraitAB is no longer object-safe1, so Rc<dyn TraitAB> no longer works. (playground link).


  1. According to the tracking issue for arbitrary self types, the object safety question is still open. I'm not really sure what the rules are right now.
查看更多
登录 后发表回答