在子类型上获取Rc<RefCell<dyn T>>

5

我有以下定义:

trait A {
    fn f(&self);
}

trait B: A {
// ...
}

我希望实现这样的功能:

fn convert(v: Rc<RefCell<dyn B>>) -> Rc<RefCell<dyn A>> {
}

我希望有一种返回值的方式,使其与相同对象共享,这意味着这些声明:
```python a = MyClass() b = a.my_method() ```
'b'将与'a'引用相同的对象。
let x: Rc<RefCell<dyn B>> /* = ... */;
let y = convert(Rc::clone(&x));

x.f()y.f()的调用都是在同一个对象上执行。

我应该如何实现函数convert,或者更改类型定义以拥有这种行为和子对象转换(即将其转换为子对象)。


“Clone an Rc<RefCell<MyType> trait object and cast it”(https://stackoverflow.com/q/55959384/3650362)大致上是相同的问题,但是被接受的答案并没有真正解决这个问题。 - trent
是的 @trentcl,这确实解决了问题,因为TraitAB不能成为trait-object(因为方法签名中的Self),但我很感激这个想法。 - Corebreaker
2个回答

1
Rust不支持直接向上转型Trait对象。由于Trait对象的实现方式,这在没有额外运行时工作的情况下是不可能的,因此Rust要求你自己完成这项工作。
例如,您可以这样做。
use std::rc::Rc;
use std::cell::RefCell;

trait A {
  fn f(&self) -> i32;
}

trait B: A {
}

struct Wrapper(Rc<RefCell<dyn B>>);

impl A for Wrapper {
    fn f(&self) -> i32 {
        A::f(&*self.0.borrow())
    }
}

fn convert(v: Rc<RefCell<dyn B>>) -> Rc<RefCell<dyn A>> {
    Rc::new(RefCell::new(Wrapper(v)))
}

是的,那是个好主意。但它缺少一件事,即convert返回的值必须引用作为“v”参数传递的对象。但我知道该怎么做,我将使用这个包装器的想法,但通过重新实现“Rc”类型(一个引用计数器),以便函数“convert”返回相同的引用。 - Corebreaker
convert的返回值必须引用作为“v”参数传递的对象。 如果你的意思是我们应该将“v”重新解释为“Rc<RefCell<dyn A>>”,那是不可能的,因为它们确实不是同一件事。如果您实际上需要一个dyn A(而不仅仅是A),则出于与fn convert(v: i32) -> String {...}相同的原因,您将拥有一个中间值。它们真的不是同一件事。 - Mike Graham

0

感谢你的想法,我决定妥协,遵循 Rust 规则,避免将 trait-object 用作函数convert的参数。

use std::rc::Rc;
use std::cell::RefCell;

trait A {
    fn print(&self);
}

trait B: A {
}

trait Convert {
    fn convert(it: Rc<RefCell<Self>>) -> Rc<RefCell<dyn A>>;
}

struct MyType(u32);

impl Convert for MyType {
    fn convert(it: Rc<RefCell<Self>>) -> Rc<RefCell<dyn A>> {it}
}

impl A for MyType {
    fn print(&self) {
        println!("MyType({}) as A", self.0);
    }
}

impl B for MyType {}

fn main() {
    let a: Rc<RefCell<dyn A>>;
    {
        let b = Rc::new(RefCell::new(MyType(3)));
        a = Convert::convert(b.clone());
    }

    a.borrow().print();
}

游乐场


这段代码并没有按照原帖中所要求的那样运行 - 试着使用 let b: Rc<RefCell<dyn B>> = Rc::new(RefCell::new(MyType(3)))(你会遇到第一个错误,即 MyType 没有实现 B,但如果你修复了它,你会发现这种方法并不能满足你在问题中所要求的) - Mike Graham
@MikeGraham 是的,我知道这段代码并没有按照你在原始帖子中所要求的那样工作,我写下这个是为了妥协。由于方差问题,Rust无法实现我想要的功能(无法将RefCell<dyn B>传递给RefCell<dyn A>)。 - Corebreaker
这段代码与 B 没有任何关系,除了定义 B 之外。MyType 甚至没有实现 B - Mike Graham
谢谢 @MikeGraham,我修改了 MyType 来实现 B - Corebreaker

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