Search code examples
rusttraitscoercion

Struct won't coerce to implemented dyn Trait type in function return


I have the following Rust code

use std::{
    cell::{Ref, RefCell},
    rc::Rc,
};

trait TraitA {
    fn data(&self) -> Ref<Vec<Rc<dyn TraitB>>>;
}

struct A {
    data: RefCell<Vec<Rc<B>>>,
}

impl TraitA for A {
    fn data(&self) -> Ref<Vec<Rc<dyn TraitB>>> {
        self.data.borrow()
    }
}

trait TraitB {}

struct B {}

impl TraitB for B {}

However I get the following error

error[E0308]: mismatched types
  --> src/lib.rs:16:9
   |
15 |     fn data(&self) -> Ref<Vec<Rc<dyn TraitB>>> {
   |                       ------------------------ expected `Ref<'_, Vec<Rc<(dyn TraitB + 'static)>>>` because of return type
16 |         self.data.borrow()
   |         ^^^^^^^^^^^^^^^^^^ expected trait object `dyn TraitB`, found struct `B`
   |
   = note: expected struct `Ref<'_, Vec<Rc<(dyn TraitB + 'static)>>>`
              found struct `Ref<'_, Vec<Rc<B>>>`

I have tried setting the data field in A to have type RefCell<Vec<Rc<dyn TraitB>>>. That will work.

However in my implementation I want to have the field contain the type B as I want the Vec to store only type B, not any implementation of TraitB.

My understanding is that Rust should be able to coerce the B type as required as long as it implements the trait TraitB, which in the example above, B does indeed implement TraitB.

For example, in the following snippet, Rc<dyn TraitB> is successfully coerced to Rc<B> no problem.

struct C {
    weak_ref: Weak<B>,
}

impl C {
    fn upgrade_weak_ref(&self) -> Rc<dyn TraitB> {
        self.weak_ref.upgrade().unwrap()
    }
}

So my question is, why is the coercion not working in the first example above. Is it because it's contained in a Ref? Is there a workaround or some way to make it work without changing the type of the data field in struct A?


Solution

  • Rc<dyn TraitB> is twice the size of Rc<B> (two usizes versus one). Therefore, there cannot be a way to coerce a Vec<Rc<B>> to Vec<Rc<dyn TraitB>> without rebuilding the vector.

    You can store Vec<Rc<dyn TraitB>>, but have only methods that push Bs and therefore protect against misuse.