文章目录
- [静态派发(Static Dispatch)](#静态派发(Static Dispatch))
- [动态派发(Dynamic Dispatch)](#动态派发(Dynamic Dispatch))
- 总结对比
在 Rust 中,静态派发(Static Dispatch) 和 动态派发(Dynamic Dispatch) 是 trait 调用的两种主要方式,涉及到 trait 的使用方式和性能之间的权衡。
下面是两者的详细对比:
静态派发(Static Dispatch)
-
定义 :在编译期就确定具体调用哪个方法,生成对应的代码(单态化 monomorphization)。
-
实现方式 :使用 泛型(generics) 实现的 trait 调用。
-
优点:
- 编译期确定类型 → 性能更好(没有运行时开销)。
- 编译器可以进行更多优化(比如内联)。
-
缺点:
- 每个泛型参数实例都会生成一份代码,可能会增加最终可执行文件大小(code bloat)。
示例:
rust
trait Speak {
fn speak(&self);
}
struct Dog;
struct Cat;
impl Speak for Dog {
fn speak(&self) {
println!("Woof!");
}
}
impl Speak for Cat {
fn speak(&self) {
println!("Meow!");
}
}
// 使用泛型参数 T(静态派发)
fn animal_speak<T: Speak>(animal: T) {
animal.speak();
}
动态派发(Dynamic Dispatch)
-
定义:在运行时通过虚表(vtable)来决定调用哪个方法。
-
实现方式 :使用 trait 对象(如
&dyn Trait
或Box<dyn Trait>
)。 -
优点:
- 类型擦除后,可以写出更灵活的代码。
- 适合在类型不确定或需要异构集合时使用。
-
缺点:
- 有运行时开销(指针跳转 + vtable)。
- trait 需要是对象安全(object-safe)。
示例:
rust
fn animal_speak(animal: &dyn Speak) {
animal.speak(); // 动态派发,通过 vtable 调用方法
}
总结对比
特性 | 静态派发 | 动态派发 |
---|---|---|
调用时机 | 编译时 | 运行时 |
性能 | 高(无额外开销) | 相对较低(有 vtable 指针开销) |
代码大小 | 可能更大(每种类型生成一份代码) | 更小(共享 trait 对象) |
使用方式 | 泛型 T: Trait |
&dyn Trait , Box<dyn Trait> |
是否需要对象安全 | 否 | 是 |