【问题标题】:Clone an Rc<RefCell<MyType> trait object and cast it克隆一个 Rc<RefCell<MyType> 特征对象并转换它
【发布时间】:2019-09-21 09:14:26
【问题描述】:

此问题与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
  • 目标是让 trait 对象 TraitA 能够转换为 TraitB,反之亦然。
  • 所以我使用了一个超特征来保存进行转换的方法。
  • 这适用于std::Rc 智能指针。

到目前为止一切顺利。但现在我需要ab 的可变引用,但由于ab 实际上是同一个类型实例,Rust 不会让我有2 个相同事物的可变引用。

所以,这类问题的常见模式是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: &amp;Self // &amp;self
  • self: &amp;mut Self // &amp;mut self
  • self: Box&lt;Self&gt; // No short form
  • self: Rc&lt;Self&gt; // No short form / Recently supported

所以这意味着我不能使用

self: Rc&lt;RefCell&lt;Self&gt;&gt;

用于自参数。

所以,主要问题是:有没有办法将 Rc&lt;RefCell&lt;TraitA&gt;&gt; 转换为 Rc&lt;RefCell&lt;TraitB&gt; ? 谢谢

【问题讨论】:

    标签: casting rust traits


    【解决方案1】:

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

    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

    【讨论】:

    • 哇。不知道特征声明和定义的“签名”可能不同。有时我有这种感觉,我正在看着 Rust 非常模糊的角落。
    • 我认为这个解决方案有一个缺点。由于我无法创建 TraitAB 特征对象,这意味着需要在编译时知道 MyType。因此,如果我有多种类型,例如MyType1MyType2 等,那么声明这样的变量将不起作用:let ab: Rc&lt;RefCell&lt;TraitAB&gt;&gt;; 你认为也可以解决这个问题吗?
    • @dospro 为了能够制作特征对象,它必须至少有一个非静态方法(即带有接收器的方法)。只需向 trait 添加一个采用 self&amp;self&amp;mut self 的方法即可实现这一目标。
    • 我尝试添加一个接收&amp;self 但没有任何区别的虚拟方法。我尝试将 Self 绑定到大小:fn as_a(this: Rc&lt;RefCell&lt;Self&gt;&gt;) -&gt; Rc&lt;RefCell&lt;dyn TraitA&gt;&gt; where Self: Sized; 但同样,我得到:the size for values of type dyn TraitAB` 在编译时无法知道`我没有想法。
    • 抱歉,我之前的评论不准确。 所有方法,不仅仅是一个或多个,一个特征对象可以使用必须是非静态的。如果必须使用TraitAB trait 对象,最好通过实现TraitATraitB (T: TraitX + ?Sized) 和TraitAB (T: TraitA + TraitB) 来抽象出RefCell 的用法987654351@,其中 RefCell&lt;T&gt; 的方法 impl 调用 RefCell 借用值上的相应方法。然后,特征对象将被包装在 Rc&lt;&gt; 中,而不是 Rc&lt;RefCell&lt;&gt;&gt; 中。 play.rust-lang.org/?gist=29bece160f90087629b0fc32a8ed0222
    猜你喜欢
    • 1970-01-01
    • 2023-04-01
    • 2021-04-06
    • 1970-01-01
    • 2021-02-04
    • 1970-01-01
    • 2015-09-16
    • 2015-10-13
    • 1970-01-01
    相关资源
    最近更新 更多