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
?
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 B
s and therefore protect against misuse.