4

I am trying to cast Rc<RefCell<Data>> to Rc<RefCell<dyn Interface>> (Data implements Interface) but it's impossible in a generic method:

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

trait Interface {
    fn pouet(&self);
}

struct Data {}

impl Interface for Data {
    fn pouet(&self) {
        println!("pouet");
    }
}

fn helper<T>(o: &Rc<RefCell<T>>)
where
    T: Interface,
{
    let t = o as &Rc<RefCell<dyn Interface>>;
    work(t);
}

fn work(o: &Rc<RefCell<dyn Interface>>) {
    o.borrow().pouet();
}

fn main() {
    // work
    {
        let o = Rc::new(RefCell::new(Data {}));
        work(&(o as Rc<RefCell<dyn Interface>>));
    }
    // raise an compile error
    {
        let o = Rc::new(RefCell::new(Data {}));
        helper(&o);
    }
}

I have an compile error on non-primitive cast :

error[E0605]: non-primitive cast: `&Rc<RefCell<T>>` as `&Rc<RefCell<dyn Interface>>`
  --> src/main.rs:20:13
   |
20 |     let t = o as &Rc<RefCell<dyn Interface>>;
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object

playground

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 3
    Unlike languages like Java, casts to trait objects actually modify the data (a vtable must be added in), which means that you have to borrow the refcell. – Aplet123 Dec 29 '20 at 16:21
  • I don't understand your answer... it is a cast not a borrow issue. I missed something ? – Vaillant Etienne Dec 29 '20 at 16:48
  • 1
    Casting the value is not simply reinterpreting the type, thus you must actually borrow the RefCell so you can mutate the value. – Aplet123 Dec 29 '20 at 16:49
  • i update my code with mu value, but i have the same problem :```fn helper(o: &mut Rc>) where T: Interface, { let t = o as &mut Rc>; work(t); }``` – Vaillant Etienne Dec 29 '20 at 16:53
  • 1
    See also [Clone an Rc trait object and cast it](https://stackoverflow.com/q/55959384/155423) – Shepmaster Dec 29 '20 at 17:00
  • Honestly I am a watching this, and I am not understanding your answers. – TheCoolDrop Dec 29 '20 at 17:07
  • @Shepmaster what it is the link with my issue ? – Vaillant Etienne Dec 29 '20 at 18:05
  • 2
    The problem is that normally, structs just contain their data. For example, `struct Foo { field: u8 }` has a size of 1 byte. However, in order to use our object as a trait object (treating it as just some random object implementing the trait), we must now store it with a table of functions that describe how to use it, called a vtable. A vtable is just a table that points to our implementation for the trait. – Coder-256 Dec 30 '20 at 04:34
  • 1
    Let's say we have a trait `MyTrait` with a method `run()`, we have structs `Foo` and `Bar` implementing `MyTrait`, and then we have a `Vec>` containing many `Foo`s and `Bar`s. Each trait object (`dyn MyTrait`) must contain a pointer to its version of `run()` so we know how to call it. So back to the question, the problem that is you need to store a table of pointers to all the methods in the trait in order to be able to use it. – Coder-256 Dec 30 '20 at 04:38
  • @Coder-256 ok i understand your explanation but it is not my problem (or i missed something ?). my probleme is `let o = Rc::new(RefCell::new(Data {}));` compile without error but the same line with generic raise an error `let t = o as &Rc>;` – Vaillant Etienne Dec 30 '20 at 09:41
  • 2
    Yes because `o` is just a pointer to an instance of `Data` when you want it to be an instance of `dyn Interface`. `dyn Interface` consists of a vtable plus the “actual” type (so in this case, a vtable plus an instance of `Data`). One way to look at is: you have a pointer to `Data`, and you are trying to cast it as a pointer to `dyn Interface` (a vtable plus `Data`), so of course it fails since they are not the same. – Coder-256 Dec 30 '20 at 09:47
  • Hey @Coder-256 can one actually even do this? I can imagine plenty of scenarios where I have concrete types, but I would like to cast them to trait objects. How would one do it in this case? Do we have to clone the data ? – TheCoolDrop Dec 30 '20 at 10:59
  • 1
    Yes, or at the very least you have to move it. A trait object works exactly like: `struct TraitObject { vtable: VTable, data: T }`, so you have to clone/move the original data in order to make it into a trait object. – Coder-256 Dec 30 '20 at 12:03

1 Answers1

1

many thanks, i understand

the solutions are

fn helper<T>(o: &Rc<RefCell<T>>)
where
    T: Interface + 'static,
{
    let t = o.clone() as Rc<RefCell<dyn Interface>>;
    work(&t);
}

or

fn helper<T>(o: Rc<RefCell<T>>)
where
    T: Interface + 'static,
{
    let t = o as Rc<RefCell<dyn Interface>>;
    work(&t);
}

Thanks