如何克隆一个Rc trait对象并将其转换为另一个trait对象?

3
这是一个关于 Rust dynamic cast trait object between different taits 的后续问题。当我们用引用来处理 trait 对象时,那里提供的解决方案非常有效,但是这一次我尝试使用 Rc 指针做同样的事情。例如:
  • 我有一个名为 TraitAB 的超级 trait 和两个名为 TraitATraitB 的 trait。
  • 因此,当我首次创建类型为 TraitAB 的 trait 对象时,不再使用 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();
    }
}

但是这样做行不通。根据错误信息:

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_aas_b无法知道自身实际上是一个Rc指针。是否有一种方法可以对克隆的共享指针进行转换?

2个回答

5

方法as_aas_b不能确定self实际上是一个Rc指针。

实际上,这并不正确!有一个很少使用的特性,可以允许将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_b会移动self: Rc<Self>,因为Rc<T>没有实现Copy(只有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不再是对象安全的1,因此Rc<dyn TraitAB>不再起作用。(playground link)
根据任意self类型的跟踪问题,对象安全性问题仍未解决。我现在不太确定规则是什么。

我现在有点困惑。在你提到的例子中,ab.clone().as_a() 是否等同于 Rc::clone(&ab).as_a() - dospro
是的。这里完全一样。只要没有歧义,您可以像调用类型自身的方法一样调用特质方法。 - SCappella

1

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

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,但你也可以为 Rc<MyType> 扩展和实现 TraitATraitB。这里有一个 工作示例,其中包含了对 TraitATraitB 的实现函数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接