【问题标题】:How do I clone a Rc trait object and cast it to another trait object?如何克隆 Rc 特征对象并将其转换为另一个特征对象?
【发布时间】:2019-04-30 19:45:41
【问题描述】:

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

  • 我有一个名为TraitAB 的超级特征和两个名为TraitA 和TraitB 的特征
  • 所以当我第一次创建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();
    }
}

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

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 指针。 有没有办法对克隆的共享指针进行强制转换?

【问题讨论】:

    标签: casting rust smart-pointers trait-objects


    【解决方案1】:

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

    事实上,这不是真的! a rarely used feature 允许将 self 视为各种标准类型的引用(Rc&lt;Self&gt;、Box&lt;Self&gt; 等)。

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

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

    不幸的是,正如所写,as_a 和 as_b 移动 self: Rc&lt;Self&gt;,因为 Rc&lt;T&gt; doesn't implement 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_a 和as_b 将self 视为&amp;Rc&lt;Self&gt;(这对我来说很奇怪,因为它是对引用的引用)。这允许在不移动 ab 的情况下调用 ab.as_a()。这种方法的唯一问题是TraitABis no longer object-safe1,所以Rc&lt;dyn TraitAB&gt; 不再起作用。 (playground link).


    1. 根据the tracking issue for arbitrary self types,对象安全问题仍然悬而未决。我不确定现在的规则是什么。

    【讨论】:

    • 我现在有点困惑。在您提到的示例中,ab.clone().as_a() 是否与Rc::clone(&amp;ab).as_a() 相同?
    • 是的。这里完全一样。只要没有歧义,您就可以像调用类型本身的方法一样调用 trait 方法。
    【解决方案2】:

    你需要在RC&lt;MyType&gt;上实现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,但你也可以扩展和实现TraitA 和TraitB 为Rc&lt;MyType&gt;。

    This is a working example 实现了TraitA 和TraitB 的功能。

    【讨论】:

      猜你喜欢
      • 2019-09-21
      • 1970-01-01
      • 1970-01-01
      • 2019-10-23
      • 1970-01-01
      • 1970-01-01
      • 2023-04-01
      • 1970-01-01
      • 2021-05-24
      相关资源
      最近更新 更多