这段代码工作正常(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()
}
但如果我创建一个包装器来生成如下所示的Rc,编译器会抱怨缺少大小的特征(playground)
fn make_foo() -> Rc<dyn Foo> {
Rc::new(Zzz::new())
}
fn main() {
let a = Bar::new(make_foo());
a.v.as_ref().foo()
}
在这两种情况下,Bar :: new接收的参数类型为Rc,为什么Rust编译器的反应不同?
默认情况下,假定所有类型变量都是Sized
。例如,在Bar
结构的定义中,有一个隐含的Sized
约束,如下所示:
struct Bar<T: Sized> {
v: Rc<T>,
}
对象dyn Foo
不能是Sized
,因为Foo
的每个可能的实现可能具有不同的大小,因此没有一个可以选择的大小。但是你试图实例化一个Bar<dyn Foo>
。
修复是选择退出Sized
的T
特征:
struct Bar<T: ?Sized> {
v: Rc<T>,
}
并且在实现的上下文中:
impl<T: ?Sized> Bar<T>
where
T: Foo
?Sized
实际上不是约束,而是放宽现有的Sized
约束,因此不需要它。
选择退出Sized
的结果是,Bar
块中的impl
方法都不能使用T
,除非通过引用。