5

我有以下代码

extern crate rand;
use rand::Rng;

pub struct Randomizer {
    rand: Box<Rng>,
}

impl Randomizer {
    fn new() -> Self {
        let mut r = Box::new(rand::thread_rng()); // works
        let mut cr = Randomizer { rand: r };
        cr
    }

    fn with_rng(rng: &Rng) -> Self {
        let mut r = Box::new(*rng); // doesn't work
        let mut cr = Randomizer { rand: r };
        cr
    }
}

fn main() {}

它抱怨说

error[E0277]: the trait bound `rand::Rng: std::marker::Sized` is not satisfied
  --> src/main.rs:16:21
   |
16 |         let mut r = Box::new(*rng);
   |                     ^^^^^^^^ `rand::Rng` does not have a constant size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `rand::Rng`
   = note: required by `<std::boxed::Box<T>>::new`

我不明白为什么它需要Sizedon RngwhenBox<T>不强加于T.

4

3 回答 3

6

更多关于Sizedtrait 和 bound - 这是一个相当特殊的 trait,它被隐式地添加到每个函数中,这就是为什么你看不到它在原型中列出的原因Box::new

fn new(x: T) -> Box<T>

请注意,它x按值(或移动)取值,因此您甚至需要知道调用该函数的大小。

相反,类型Box本身不需要; Sized它使用(再次特殊的)特征 bound ?Sized,这意味着“选择退出默认Sized边界”:

pub struct Box<T> where T: ?Sized(_);

如果您仔细查看,有一种方法可以创建一个未调整大小Box的类型:

impl<T> Box<T> where T: ?Sized
....
    unsafe fn from_raw(raw: *mut T) -> Box<T>

因此,从不安全的代码中,您可以从原始指针创建一个。从那时起,所有正常的事情都起作用了。

于 2016-08-30T07:33:14.337 回答
3

问题其实很简单:你有一个 trait 对象,关于这个 trait 对象你只知道两件事:

  • 它的可用方法列表
  • 指向其数据的指针

当您请求将此对象移动到不同的内存位置(此处在堆上)时,您会丢失一个关键信息:它的大小

你怎么知道应该保留多少内存?要移动多少位?

当一个对象是Sized时,此信息在编译时是已知的,因此编译器会为您“注入”它。然而,在 trait-object 的情况下,此信息是未知的(不幸的是),因此这是不可能的。

提供此信息并提供多态移动/克隆将非常有用,但这还不存在,到目前为止我不记得有任何建议,我不知道成本是多少(在维护,运行时间损失,...)。

于 2016-08-30T00:07:16.787 回答
1

我也想发布答案,处理这种情况的一种方法是

fn with_rng<TRand: Rng>(rng: &TRand) -> Self {
    let r = Box::new(*rng);
    Randomizer { rand: r }
}

Rust 的单态性将创建用具体大小的类型with_rng替换的必要实现。TRand此外,您可以添加一个要求TRand为的特征绑定Sized

于 2016-09-01T20:29:59.443 回答