静态派发与动态派发 Static Dispatch and Dynamic Dispatch
一句话概括它们的特点:
- 静态派发 (static dispatch), 是在编译期间确定函数调用关系, 是所谓的编译期多态 compiling time polymorphism
- 动态派发 (dynamic dispatch), 是在程序运行期间, 通过对象的虚表 (vtable), 找到要调用的目标函数, 并调用它, 是所谓的运行时多态 runtime polymorphism
可以看到, 动态派发多了查找虚表的操作, 要比静态派发慢一点点, 但是动态派发使用起来更加灵活.
举例来说:
pub struct Point {
x: f32,
y: f32,
}
pub trait Print {
fn print(&self);
fn default_method(&self) {
println!("Print::default_method()");
}
}
impl dyn Print + 'static {
fn print_trait_object(&self) {
println!("Print::print_trait_object()");
}
}
impl Print for Point {
fn print(&self) {
println!("Point({}, {})", self.x, self.y);
}
}
fn static_dispatch<T: Print>(point: &T) {
print!("static_dispatch: ");
point.print();
point.default_method();
}
fn dynamic_dispatch(point: &(dyn Print + 'static)) {
print!("dynamic_dispatch: ");
point.print();
point.default_method();
point.print_trait_object();
}
fn main() {
let p = Point { x: 3.0, y: 4.0 };
static_dispatch(&p);
println!("==============");
dynamic_dispatch(&p);
}
impl trait
有时候使用 trait 作为函数返回值的类型:
#![allow(dead_code)]
mod static_dispatch_input;
fn double_positive(numbers: &[i32]) -> impl Iterator<Item=i32> + '_ {
numbers.iter().filter(|x| x > &&0).map(|x| x + 2)
}
fn combine_vecs(u: Vec<i32>, v: Vec<i32>) -> impl Iterator<Item=i32> {
v.into_iter().chain(u).cycle()
}
fn make_adder_function(y: i32) -> impl Fn(i32) -> i32 {
move |x: i32| x + y
}
fn main() {}
只要 trait 或者相应的类型在本 crate 中有声明, 就可以给这个类型实现这个 trait.
作为传入参数
先看一个例子, 用静态派发的方式将 trait 作为函数的传入参数:
接下来我将把这个例子改造成动态派发的形式:
作为函数返回值
先看一个例子, 用静态派发的方式将 trait 作为函数的返回值:
接下来我将把这个例子改造成动态派发的形式:
# 参考
- [trait object](https://stackoverflow.com/questions/27567849/what-makes-something-a-trait-object)