克隆Rc <refcell <mytype>特征对象并将其强制转换

时间:2019-05-02 19:56:34

标签: casting rust traits

此问题与Rust: Clone and Cast Rc pointer

有关

让我说这段代码运行良好:

use std::rc::Rc;

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

trait TraitA {}
trait TraitB {}

struct MyType {}

impl TraitAB for MyType {
    fn as_a(self: Rc<Self>) -> Rc<dyn TraitA> {self}
    fn as_b(self: Rc<Self>) -> Rc<dyn TraitB> {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.clone().as_a();
        b = ab.clone().as_b();
    }
    // Use a and b.
}

稍微解释一下代码:

  • 我有一个名为MyType的类型,它实现了TraitATraitB
  • 目标是使特征对象TraitA能够强制转换为TraitB,反之亦然。
  • 因此,我使用一个超级特征来保存进行转换的方法。
  • 这对std::Rc个智能指针非常有用。

到目前为止,一切都很好。但是现在我需要同时引用ab的可变引用,但是由于ab实际上是相同的类型实例,Rust不会让我有两个可变的引用同一件事的引用。

因此,这类问题的常见模式是std::cell::RefCell

注意:我认为这种模式在特定情况下是正确的,因为它是常见的内部可变性问题。我不愿意实际更改引用,而只更改类型的内部状态。

因此,按照这个想法,我更改了以下几行:

trait TraitAB : TraitA + TraitB {
    fn as_a(self: Rc<RefCell<Self>>) -> Rc<RefCell<dyn TraitA>>;
    fn as_b(self: Rc<RefCell<Self>>) -> Rc<RefCell<dyn TraitB>>;
}
//...
let mut ab: Rc<RefCell<dyn TraitAB>> = Rc::new(RefCell::new(MyType{}));

但是此更改不会编译。经过一番阅读,我发现自我只能是:

  • self: Self // self
  • self: &Self // &self
  • self: &mut Self // &mut self
  • self: Box<Self> // No short form
  • self: Rc<Self> // No short form / Recently supported

所以这意味着我不能使用

self: Rc<RefCell<Self>>

关于自我参数。

因此,主要问题是:是否可以将Rc<RefCell<TraitA>>转换为Rc<RefCell<TraitB>? 谢谢

1 个答案:

答案 0 :(得分:1)

您可以通过使用TraitAB的转换方法(即,将它们声明为关联函数)的接收器来 not 来解决此问题:

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

特征可以被实现为

impl TraitAB for MyType {
    fn as_a(it: Rc<RefCell<MyType>>) -> Rc<RefCell<dyn TraitA>> {it}
    fn as_b(it: Rc<RefCell<MyType>>) -> Rc<RefCell<dyn TraitB>> {it}
}

然后可以使用完全限定的语法来调用这些函数。

a = TraitAB::as_a(ab.clone());
b = TraitAB::as_b(ab.clone());

所有类型的TraitAB实现都是相同的。要使此实现可用于实现TraitATraitB的所有类型,可以使用通用的impl

impl<T: TraitA + TraitB + 'static> TraitAB for T {
    fn as_a(it: Rc<RefCell<T>>) -> Rc<RefCell<dyn TraitA>> {it}
    fn as_b(it: Rc<RefCell<T>>) -> Rc<RefCell<dyn TraitB>> {it}
}

请注意T: 'static,因为函数返回类型中的特征对象具有隐式的'static生命周期约束。

Playground