通常如何创建一个 Box<dyn Trait> 或一个装箱的未调整大小的值?
How do you create a Box<dyn Trait>, or a boxed unsized value in general?
我有以下代码
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`
我不明白为什么在 Rng
上需要 Sized
而 Box<T>
不强加于 T
。
问题其实很简单:你有一个特征对象,关于这个特征对象你只知道两件事:
- 它的可用方法列表
- 指向其数据的指针
当您请求将此对象移动到不同的内存位置(在堆上)时,您会丢失一个关键 信息:它的大小.
你怎么知道应该保留多少内存?要移动多少位?
当对象是 Sized
时,此信息在编译时已知,因此编译器 "injects" 会为您提供。然而,在特征对象的情况下,此信息是未知的(不幸的是),因此这是不可能的。
提供此信息并提供多态 move/clone 会非常有用,但这还不存在,我不记得到目前为止有任何建议,我也不知道是什么成本将是(在维护、运行时间惩罚等方面)。
关于 Sized
特征和界限的更多信息 - 这是一个相当特殊的特征,即 implicitly added to every function, which is why you don't see it listed in the prototype for Box::new
:
fn new(x: T) -> Box<T>
请注意,它需要 x
按值(或移动),因此您需要知道它有多大才能调用该函数。
相比之下,Box
类型本身不需要需要Sized
;它使用(再次特殊的)特征边界 ?Sized
,这意味着 "opt out of the default Sized
bound":
pub struct Box<T> where T: ?Sized(_);
如果仔细查看,有一种方法可以创建 Box
类型未调整的类型:
impl<T> Box<T> where T: ?Sized
....
unsafe fn from_raw(raw: *mut T) -> Box<T>
因此,您可以从不安全代码创建一个原始指针。从此一切正常。
我也想post这个答案,处理这种情况的一种方法是
fn with_rng<TRand: Rng>(rng: &TRand) -> Self {
let r = Box::new(*rng);
Randomizer { rand: r }
}
Rust 的单态性将创建 with_rng
的必要实现,用具体大小的类型替换 TRand
。此外,您可以添加一个特征界限,要求 TRand
为 Sized
.
我有以下代码
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`
我不明白为什么在 Rng
上需要 Sized
而 Box<T>
不强加于 T
。
问题其实很简单:你有一个特征对象,关于这个特征对象你只知道两件事:
- 它的可用方法列表
- 指向其数据的指针
当您请求将此对象移动到不同的内存位置(在堆上)时,您会丢失一个关键 信息:它的大小.
你怎么知道应该保留多少内存?要移动多少位?
当对象是 Sized
时,此信息在编译时已知,因此编译器 "injects" 会为您提供。然而,在特征对象的情况下,此信息是未知的(不幸的是),因此这是不可能的。
提供此信息并提供多态 move/clone 会非常有用,但这还不存在,我不记得到目前为止有任何建议,我也不知道是什么成本将是(在维护、运行时间惩罚等方面)。
关于 Sized
特征和界限的更多信息 - 这是一个相当特殊的特征,即 implicitly added to every function, which is why you don't see it listed in the prototype for Box::new
:
fn new(x: T) -> Box<T>
请注意,它需要 x
按值(或移动),因此您需要知道它有多大才能调用该函数。
相比之下,Box
类型本身不需要需要Sized
;它使用(再次特殊的)特征边界 ?Sized
,这意味着 "opt out of the default Sized
bound":
pub struct Box<T> where T: ?Sized(_);
如果仔细查看,有一种方法可以创建 Box
类型未调整的类型:
impl<T> Box<T> where T: ?Sized
....
unsafe fn from_raw(raw: *mut T) -> Box<T>
因此,您可以从不安全代码创建一个原始指针。从此一切正常。
我也想post这个答案,处理这种情况的一种方法是
fn with_rng<TRand: Rng>(rng: &TRand) -> Self {
let r = Box::new(*rng);
Randomizer { rand: r }
}
Rust 的单态性将创建 with_rng
的必要实现,用具体大小的类型替换 TRand
。此外,您可以添加一个特征界限,要求 TRand
为 Sized
.