为什么生成器函数生成Rc <t>需要大小特征?

时间:2019-03-08 10:52:34

标签: rust traits smart-pointers trait-objects

此代码可以正常工作(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编译器的反应不同?

1 个答案:

答案 0 :(得分:2)

默认情况下,所有类型变量都假定为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