当T是特征对象时,如何创建Box <t>?

时间:2016-08-29 23:52:25

标签: rust trait-objects

我有以下代码

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`

Sized没有将Rng强加于Box<T>时,我不明白为什么T需要abstract class Foo { def f(): ??? = { ... } } class Bar extends Foo { ... } class Baz extends Foo { ... }

3 个答案:

答案 0 :(得分:5)

关于Sized特征和约束的更多信息 - 它是一个相当特殊的特征,对于每个函数都是implicitly added,这就是为什么你没有看到它在原型中列出的原因对于Box::new

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

请注意,它需要x值(或移动),所以你需要知道甚至调用函数有多大。

相反,Box类型本身需要Sized;它使用(再次特殊)特征绑定?Sized,这意味着&#34;选择退出默认的Sized绑定&#34;:

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

如果你仔细观察,有一种方法可以创建一个带有unsized类型的Box

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

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

答案 1 :(得分:3)

问题其实很简单:你有一个特质对象,你对这个特质对象知道的唯一两件事是:

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

当您请求将此对象移动到其他内存位置(此处位于堆上)时,您缺少一条关键信息:其大小

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

当对象为Sized时,此信息在编译时是已知的,因此编译器会为您“注入”它。但是,在特征对象的情况下,这些信息是未知的(遗憾的是),因此这是不可能的。

提供这些信息并提供多态移动/克隆是非常有用的,但这还不存在,我不记得到目前为止的任何提议,我不知道成本是多少(在维护,运行时惩罚等方面......)。

答案 2 :(得分:1)

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

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

Rust的单态性将创建with_rng以具体大小类型替换TRand的必要实现。此外,您可以添加要求TRandSized的特征限制。