Returning a smart pointer reference to a struct cast as a trait object

See here.

The function signature you wrote is impossible to implement (without leaking a newly created Rc).

Turning concrete types into trait object types is an operation that always only can work behind exactly one level of indirection. You can turn an owned Rc<Struct> into Rc<dyn Trait> or a reference &Struct into &dyn Trait, but you cannot turn &Rc<Struct> into &Rc<dyn Trait> behind two levels of indirection.

This is because of where the dynamic information (vtable pointer) is stored:

Rc<Struct>: [ #address ]
               | (points to)
               |
               +--+
                  |
                  v
[reference count] [ Struct’s field values ]
Rc<dyn Trait>: [ #address, #vtable_ptr ]
                  |         |
                  |         +----------------+
                  v                          |
[reference count] [ Struct’s field values ]  |
                                             |
            +--------------------------------+
            |
            v
the vtable: [ size, destructor fn-pointer, fn-pointers for methods, … ] 

So you can turn Rc<Struct> into Rc<dyn Struct> by adding the vtable pointer. Same for works references

&Struct: [ #address ]
            |
+-----------+
|
v
[ Struct’s field values ]
&dyn Trait: [ #address, #vtable_ptr ]
               |         |
+--------------+         |
|                        +--+
v                           |
[ Struct’s field values ]   |
                            |
            +---------------+
            |
            v
the vtable: [ size, destructor fn-pointer, fn-pointers for methods, … ] 

However, with two indirections

&Rc<Struct>: [ #address ]
                |
            +---+
            |
            v
Rc<Struct>: [ #address ]
               |
               |
               +--+
                  |
                  v
[reference count] [ Struct’s field values ]
&Rc<dyn Trait>: [ #address ]
                   |
               +---+
               |
               v
Rc<dyn Trait>: [ #address, #vtable_ptr ]
                  |         |
                  |         +----------------+
                  v                          |
[reference count] [ Struct’s field values ]  |
                                             |
            +--------------------------------+
            |
            v
the vtable: [ size, destructor fn-pointer, fn-pointers for methods, … ] 

you can see how turning &Rc<Struct> into &Rc<dyn Trait> can not work, because there’s no preexisting Rc<dyn Trait> anywhere in memory to point to, and the existing Rc<Struct> doesn’t have the vtable pointer in it. The &Rc<dyn Trait> you’d like to return cannot own such that Rc<dyn Trait> either, since references don’t have ownership.

The only things that do work for your function signature is either to return an owned rc Rc<dyn Trait> by cloning the Rc from the field, or a direct reference to the trait object &dyn Trait. Which one you want depends on your use-case.

6 Likes