克隆并投射Rc指针

时间:2019-04-30 19:45:41

标签: casting rust smart-pointers

这是一个跟进的问题: Rust dynamic cast trait object between different taits

当我们使用特征对象的引用时,那里提供的解决方案非常有效。

这次,我试图对Rc指针执行相同的操作。例如

  • 我有一个名为TraitAB的超级特质和两个名为TraitATraitB的特质
  • 因此,当我第一次创建类型为TraitAB的特征对象而不是使用Box时,现在我使用的是Rc指针。
  • 我需要类型为TraitA的变量作为ab的引用

这里我举了一个非常小的例子:

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();
    }
}

但这不起作用。根据错误消息:

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`

方法as_aas_b不知道self实际上是一个Rc指针。 有没有办法对克隆的共享指针进行强制转换?

谢谢

2 个答案:

答案 0 :(得分:2)

  

方法as_a和as_b不能知道self实际上是一个Rc指针。

实际上,那不是事实! a rarely used feature允许将self用作各种标准类型的引用(Rc<Self>Box<Self>等)。

这意味着您可以将TraitAB重写为

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

不幸的是,as_aas_bself: Rc<Self> doesn't implement Copy(仅Rc<T>)以来就移动了Clone。解决此问题的一种方法是在将ab传递给这些方法之前,先对其进行克隆。这也意味着您无需在方法内部克隆self(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();

使用仅夜间功能arbitrary_self_types,可以使as_aas_b取为&Rc<Self>(这对我来说很奇怪,因为它是对参考)。这样就可以在不移动ab.as_a()的情况下调用ab。这种方法的唯一问题是TraitAB is no longer object-safe 1 ,因此Rc<dyn TraitAB>不再起作用。 (playground link)


  1. 根据the tracking issue for arbitrary self types,对象安全性问题仍然存在。我不确定目前的规则是什么。

答案 1 :(得分:1)

您需要在TraitAB上实现RC<MyType>

这是代码:

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();
    }
}

我没有看到TraitAB扩展TraitA + TraitB的任何理由,但是您也可以为TraitA扩展和实现TraitBRc<MyType>

您可以在Playground

中找到包含TraitATraitB的已实现功能的工作示例。