3

这是Rust dynamic cast trait object between different taits的后续问题。当我们使用 trait 对象的引用时,那里提供的解决方案非常有效,但这次我试图对Rc指针做同样的事情。例如

  • 我有一个名为的超级特质TraitAB和两个名为TraitA和的特质TraitB
  • 所以当我第一次创建一个类型的特征对象TraitAB而不是使用 aBox时,现在我使用了一个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();
    }
}

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

error[E0308]: mismatched types
  --> src/main.rs:15:19
   |
15 |         Rc::clone(self)
   |                   ^^^^ expected struct `std::rc::Rc`, found struct `MyType`
   |
   = note: expected reference `&std::rc::Rc<dyn TraitA>`
              found reference `&MyType`

error[E0308]: mismatched types
  --> src/main.rs:18:19
   |
18 |         Rc::clone(self)
   |                   ^^^^ expected struct `std::rc::Rc`, found struct `MyType`
   |
   = note: expected reference `&std::rc::Rc<dyn TraitB>`
              found reference `&MyType`

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

4

2 回答 2

5

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

事实上,这不是真的!有一个很少使用的功能可以self被视为各种标准类型的引用(Rc<Self>Box<Self>等)。

这意味着你可以重写你的TraitABas

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

不幸的是,正如所写的那样,as_amove as_bself: Rc<Self>因为Rc<T> 没有实现Copy(仅Clone)。解决此问题的一种方法是ab在将其传递给这些方法之前简单地进行克隆。这也意味着您不需要克隆self方法内部。(游乐场链接)

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

使用 nightly-only 功能,arbitrary_self_types可以将 self 设置为(这对我来说很奇怪,因为它是对引用的引用)。这允许在不移动的情况下调用。这种方法的唯一问题是不再是对象安全的1,因此不再有效。(游乐场链接)as_aas_b&Rc<Self>ab.as_a()abTraitAB Rc<dyn TraitAB>


  1. 根据任意自我类型的跟踪问题,对象安全问题仍然悬而未决。我不确定现在的规则是什么。
于 2019-04-30T20:32:25.517 回答
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,但你也可以扩展TraitATraitB实现Rc<MyType>

这是一个工作示例,其中实现了TraitA和的功能TraitB

于 2019-04-30T20:34:39.267 回答