是什么
在 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