5

I have the following definitions:

trait A {
    fn f(&self);
}

trait B: A {
// ...
}

I'd like implement this kind of function:

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

I'd like to have a way for returning a value that share the same object, that's means that with these declarations:

let x: Rc<RefCell<dyn B>> /* = ... */;
let y = convert(Rc::clone(&x));

The calls x.f() and y.f() apply the call on the same object.

How can i implement the function convert or how can change the type definitions to have that behaviour and that kind of conversion (a conversion to a sub-object).

Corebreaker
  • 357
  • 1
  • 11
  • [Clone an Rc trait object and cast it](https://stackoverflow.com/q/55959384/3650362) is approximately the same question, but the accepted answer does not really solve the problem. – trent Oct 17 '20 at 00:42
  • Yest @trentcl, this does solve the problem cause the TraitAB can't be an trait-object (cause the Self in method signature), but i appreciate the idea. – Corebreaker Oct 17 '20 at 08:31

2 Answers2

1

Rust does not support direct upcasting of trait objects. Due to the way trait objects are implemented, this is not possible without extra work at runtime, so Rust makes you do the work yourself.

You can do it like e.g.

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)))
}
Mike Graham
  • 73,987
  • 14
  • 101
  • 130
  • Yes, that good idea. But it misses a thing, the returned value of convert must refer to the object passed as the `v` parameter. But i know how to do, i'll use this idea of wrapper but by re-implementing the `Rc` type (a reference counter) in order to have the same reference returned by the function `convert`. – Corebreaker Oct 17 '20 at 08:09
  • """ the returned value of convert must refer to the object passed as the `v` parameter""" If you mean we should reinterpret `v` as an `Rc>`, that is *not possible* since they really, truly aren't the same thing. If you actually need a `dyn A` (and not just an A), you *will* have an intermediate value, for the same reason that `fn convert(v: i32) -> String {...}` has to have an intermediate value. They really, truly aren't the same thing. – Mike Graham Oct 17 '20 at 20:05
0

Thank for your ideas, i decided to use a compromise by avoiding the use of a trait-object as parameter of the function convert in according to Rust rules:

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();
}

Playground

Corebreaker
  • 357
  • 1
  • 11
  • This code does not do what you you asked for in the original post - try doing `let b: Rc> = Rc::new(RefCell::new(MyType(3)))` (The first error you'll hit is that `MyType` does not impl `B`, but if you fix that you'll see that this approach does not do what you asked for in your question) – Mike Graham Oct 17 '20 at 20:06
  • @MikeGraham Yes, i know that this code does not do what you you asked for in the original pos, i wrote that's a compromise. Rust can't do what i wanted (no way to pass `RefCell` to `RefCell`) cause the variance. – Corebreaker Oct 19 '20 at 11:30
  • This code does not involve `B` in any way, other than to define `B`. `MyType` doesn't even impl `B`. – Mike Graham Oct 20 '20 at 00:25
  • Thank you @MikeGraham, i modified `MyType` for implementing `B`. – Corebreaker Oct 21 '20 at 07:44