<T: Trait> Box<T> 和 &Trait / Box<Trait> 有什么区别?
What is the difference between <T: Trait> Box<T> and &Trait / Box<Trait>?
编写带有特征的代码时,您可以将特征放在特征边界中:
use std::fmt::Debug;
fn myfunction1<T: Debug>(v: Box<T>) {
println!("{:?}", v);
}
fn myfunction2<T: Debug>(v: &T) {
println!("{:?}", v);
}
fn main() {
myfunction1(Box::new(5));
myfunction2(&5);
}
或直接在 Box
或引用类型中:
use std::fmt::Debug;
fn myfunction3(v: Box<Debug>) {
println!("{:?}", v);
}
fn myfunction4(v: &Debug) {
println!("{:?}", v);
}
fn main() {
myfunction3(Box::new(5));
myfunction4(&5);
}
这些给出了相同的输出。那有什么区别呢?
(这个问题的灵感来自 another question,其中这只是几个混合概念之一)
对于 <T: Trait> Box<T>
,您正在使用特征绑定来告诉编译器您想要一个 Box
以及实现 Trait
的某种类型 T
的实例,并且使用时指定T
。 Rust 编译器可能会为代码中的每个不同 T
创建不同的、高效的代码(单态化)。
使用 Box<Trait>
你是在告诉编译器你想要一个 Box
和一个特征对象,一个指向实现 unknown 类型的指针15=],这意味着编译器将使用动态调度。
我提供了两个示例,使区别更加清晰:
<T: Trait> Box<T>
,即特征界限:
use std::fmt::Debug;
struct Wrapper<T> {
contents: Option<Box<T>>,
}
impl<T: Debug> Wrapper<T> {
fn new() -> Wrapper<T> {
Wrapper { contents: None }
}
fn insert(&mut self, val: Box<T>) {
}
}
fn main() {
let mut w = Wrapper::new();
// makes T for w be an integer type, e.g. Box<i64>
w.insert(Box::new(5));
// type error, &str is not an integer type
// w.insert(Box::new("hello"));
}
Box<Trait>
,即特征对象:
use std::fmt::Debug;
struct Wrapper {
contents: Option<Box<Debug>>,
}
impl Wrapper {
fn new() -> Wrapper {
Wrapper { contents: None }
}
fn insert(&mut self, val: Box<Debug>) {
}
}
fn main() {
let mut w = Wrapper::new();
w.insert(Box::new(5));
w.insert(Box::new("hello"));
}
有关特征边界和特征对象之间差异的更多详细信息,我推荐 the section on trait objects in the first edition of the Rust book。
重要的是,你没有有将泛型类型放在引用后面(如&
或Box
),你可以直接接受它:
fn myfunction3<T: Debug>(v: T) {
println!("{:?}", v);
}
fn main() {
myfunction3(5);
}
这具有单态化相同的好处,没有额外内存分配的缺点 (Box
) 或需要在某处保留值的所有权 (&
)。
我想说泛型通常应该是默认选择——当存在动态调度/异质性时,你只需要一个 trait 对象。
编写带有特征的代码时,您可以将特征放在特征边界中:
use std::fmt::Debug;
fn myfunction1<T: Debug>(v: Box<T>) {
println!("{:?}", v);
}
fn myfunction2<T: Debug>(v: &T) {
println!("{:?}", v);
}
fn main() {
myfunction1(Box::new(5));
myfunction2(&5);
}
或直接在 Box
或引用类型中:
use std::fmt::Debug;
fn myfunction3(v: Box<Debug>) {
println!("{:?}", v);
}
fn myfunction4(v: &Debug) {
println!("{:?}", v);
}
fn main() {
myfunction3(Box::new(5));
myfunction4(&5);
}
这些给出了相同的输出。那有什么区别呢?
(这个问题的灵感来自 another question,其中这只是几个混合概念之一)
对于 <T: Trait> Box<T>
,您正在使用特征绑定来告诉编译器您想要一个 Box
以及实现 Trait
的某种类型 T
的实例,并且使用时指定T
。 Rust 编译器可能会为代码中的每个不同 T
创建不同的、高效的代码(单态化)。
使用 Box<Trait>
你是在告诉编译器你想要一个 Box
和一个特征对象,一个指向实现 unknown 类型的指针15=],这意味着编译器将使用动态调度。
我提供了两个示例,使区别更加清晰:
<T: Trait> Box<T>
,即特征界限:
use std::fmt::Debug;
struct Wrapper<T> {
contents: Option<Box<T>>,
}
impl<T: Debug> Wrapper<T> {
fn new() -> Wrapper<T> {
Wrapper { contents: None }
}
fn insert(&mut self, val: Box<T>) {
}
}
fn main() {
let mut w = Wrapper::new();
// makes T for w be an integer type, e.g. Box<i64>
w.insert(Box::new(5));
// type error, &str is not an integer type
// w.insert(Box::new("hello"));
}
Box<Trait>
,即特征对象:
use std::fmt::Debug;
struct Wrapper {
contents: Option<Box<Debug>>,
}
impl Wrapper {
fn new() -> Wrapper {
Wrapper { contents: None }
}
fn insert(&mut self, val: Box<Debug>) {
}
}
fn main() {
let mut w = Wrapper::new();
w.insert(Box::new(5));
w.insert(Box::new("hello"));
}
有关特征边界和特征对象之间差异的更多详细信息,我推荐 the section on trait objects in the first edition of the Rust book。
重要的是,你没有有将泛型类型放在引用后面(如&
或Box
),你可以直接接受它:
fn myfunction3<T: Debug>(v: T) {
println!("{:?}", v);
}
fn main() {
myfunction3(5);
}
这具有单态化相同的好处,没有额外内存分配的缺点 (Box
) 或需要在某处保留值的所有权 (&
)。
我想说泛型通常应该是默认选择——当存在动态调度/异质性时,你只需要一个 trait 对象。