是什么
在 Rust 中,dyn
关键字用于显式标记一个 trait 对象。Trait 对象是一种允许对不同类型的值进行统一处理的动态分发机制,它们通过某个共同的 trait 来进行抽象。
在 Rust 2015 版或更早的版本中,dyn
关键字不是必需的。但是,从 Rust 2018 版开始,为了提高代码的可读性和一致性,Rust 推荐使用 dyn
关键字来明确地表示 trait 对象。
作用
类型安全:
dyn
明确表示编译器在这里使用动态分发。因此,使用dyn
Trait 时,Rust 会在运行时而不是编译时解析方法调用,这允许我们在单个变量中存储实现了同一 trait 的不同类型的值。清晰性:在代码中显式区分动态分发和静态分发(例如,通过泛型)有助于理解代码的行为,尤其是在复杂的项目中。
向前兼容:明确区分动态和静态分发可以帮助未来的 Rust 版本在不破坏现有代码的情况下引入新的功能或变化。
例子
trait Speak {
fn say(&self);
}
struct Dog;
struct Cat;
impl Speak for Dog {
fn say(&self) {
println!("Woof!");
}
}
impl Speak for Cat {
fn say(&self) {
println!("Meow!");
}
}
fn make_noise(animal: &dyn Speak) {
animal.say();
}
fn main() {
let dog = Dog;
let cat = Cat;
make_noise(&dog);
make_noise(&cat);
}
不使用
在上面的例子中,如果你省略 dyn
关键字,你不会得到一个编译错误,因为从 Rust 2018 edition 开始,dyn
关键字是可选的,但是强烈建议使用它来编写更清晰的代码。使用 dyn
关键字可以明确地表明函数参数是一个动态分发的特征对象。
如果你省略了 dyn
关键字,代码仍然会工作,因为在 Rust 2015 edition 中,这是默认的行为,而在 Rust 2018 edition 及以后,它是允许但不推荐的做法。不过,你的代码可能会不太清晰,因为它不明确地表明特征对象是动态分发的。
下面是省略 dyn
关键字的代码:
trait Speak {
fn say(&self);
}
// ...(其他代码和之前一样)
fn make_noise(animal: &Speak) {
animal.say();
}
// ...(其他代码和之前一样)
fn make_noise<T: Speak>(animal: &T) {
animal.say();
}
在这种情况下,Rust 编译器会为传递给 make_noise
函数的每种类型生成不同的代码实例。这种方法在编译时需要知道所有可能的类型,并且不会有运行时的性能开销,因为不需要通过虚拟方法表来动态查找方法。
性能差别
- 静态分发:155.601083ms
- 动态分发:153.239916ms