This code works fine (playground):
use std::rc::Rc;
trait Foo {
fn foo(&self);
}
struct Bar<T> {
v: Rc<T>,
}
impl<T> Bar<T> where
T: Foo {
fn new(rhs: Rc<T>) -> Bar<T> {
Bar{v: rhs}
}
}
struct Zzz {
}
impl Zzz {
fn new() -> Zzz {
Zzz{}
}
}
impl Foo for Zzz {
fn foo(&self) {
println!("Zzz foo");
}
}
fn make_foo() -> Rc<Foo> {
Rc::new(Zzz{})
}
fn main() {
let a = Bar::new(Rc::new(Zzz::new()));
a.v.as_ref().foo()
}
but if I make a wrapper to generate Rc like below, the compiler complains about missing sized trait (playground)
fn make_foo() -> Rc<dyn Foo> {
Rc::new(Zzz::new())
}
fn main() {
let a = Bar::new(make_foo());
a.v.as_ref().foo()
}
in both cases, Bar::new received parameters with same type Rc, why the rust compiler reacts different?
By default, all type variables are assumed to be Sized
. For example, in the definition of the Bar
struct, there is an implicit Sized
constraint, like this:
struct Bar<T: Sized> {
v: Rc<T>,
}
The object dyn Foo
cannot be Sized
since each possible implementation of Foo
could have a different size, so there isn't one size that can be chosen. But you are trying to instantiate a Bar<dyn Foo>
.
The fix is to opt out of the Sized
trait for T
:
struct Bar<T: ?Sized> {
v: Rc<T>,
}
And also in the context of the implementations:
impl<T: ?Sized> Bar<T>
where
T: Foo
?Sized
is actually not a constraint, but relaxing the existing Sized
constraint, so that it is not required.
A consequence of opting out of Sized
is that none of Bar
's methods from that impl
block can use T
, except by reference.