为什么构建器函数需要大小特征来生成Rc ?

问题描述 投票:1回答:1

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

rust traits smart-pointers trait-objects
1个回答
2
投票

默认情况下,假定所有类型变量都是Sized。例如,在Bar结构的定义中,有一个隐含的Sized约束,如下所示:

struct Bar<T: Sized> {
    v: Rc<T>,
}

对象dyn Foo不能是Sized,因为Foo的每个可能的实现可能具有不同的大小,因此没有一个可以选择的大小。但是你试图实例化一个Bar<dyn Foo>

修复是选择退出SizedT特征:

struct Bar<T: ?Sized> {
    v: Rc<T>,
}

并且在实现的上下文中:

impl<T: ?Sized> Bar<T>
where 
    T: Foo 

?Sized实际上不是约束,而是放宽现有的Sized约束,因此不需要它。

选择退出Sized的结果是,Bar块中的impl方法都不能使用T,除非通过引用。

© www.soinside.com 2019 - 2024. All rights reserved.