Eri*_*aas 10 traits rust trait-objects
在编写具有特征的代码时,您可以将特征置于特征界限中:
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);
}
Run Code Online (Sandbox Code Playgroud)
或直接使用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);
}
Run Code Online (Sandbox Code Playgroud)
这些输出相同.那么区别是什么呢?
(这个问题的灵感来自另一个问题,这只是几个混合概念中的一个)
Eri*_*aas 10
随着<T: Trait> Box<T>你使用必然会告诉你想要一个编译器的性状Box与一些类型的实例T,它实现Trait了,你会指定T当您使用它.Rust编译器可能会为代码中的每个不同代码创建不同的,高效T的代码(单态化).
随Box<Trait>你告诉编译器你想要一个Box特征对象,一个指向实现的未知类型的指针Trait,这意味着编译器将使用动态调度.
我已经包含了两个例子,它们使得差异更加清晰:
<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"));
}
Run Code Online (Sandbox Code Playgroud)
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"));
}
Run Code Online (Sandbox Code Playgroud)
有关特征边界和特征对象之间差异的更多详细信息,我推荐第一版Rust书中关于特征对象的部分.
重要的是,你不要有把泛型类型的引用(如后面&或Box),您可以直接接受它:
fn myfunction3<T: Debug>(v: T) {
println!("{:?}", v);
}
fn main() {
myfunction3(5);
}
Run Code Online (Sandbox Code Playgroud)
这具有与单态化相同的好处,而没有额外内存分配 ( Box) 或需要在某处保留值的所有权( )的缺点&。
我会说泛型通常应该是默认选择——当存在动态调度/异构时,你只需要一个trait 对象。