Rust的运行时多态
Rust的静态多态即编译时多态,通过**泛型+特征约束(Generic Type + Trait Constrait)**来实现;
那么动态多态(运行时多态)呢?答案是特征对象(Trait Object)。
特征对象不是一个实例,而是一个结构体类型。
语法是dyn TraitName
,编译时指示某个对象是实现TraitName的类型,其具体类型未知。
分析
程序运行时,要调用一个特征方法,需要两个要素:
- 对象实例(对象类型未知,因此其编译期大小未知)
- 特征的方法表
所以特征对象结构体必须获知以上两个要素。
发生运行时多态时,在编译阶段编译器无法辨别对象实例的类型,因此对象实例要素的大小无法获知,进而特征对象(Trait Object)的大小在编译阶段无法被确定,这决定了Trait Object只能存放在堆上,通过引用或智能指针来访问。
指向特征对象的引用或智能指针,包含了两个指针成员,在程序运行时,ptr1在指向对象实例,ptr2指向该对象类型的Trait Method实现。
典型用例
rust
#[derive(Debug)]
struct Journal {
author: String,
year: u16,
from: String,
}
#[derive(Debug)]
struct Conference {
author: String,
year: u16,
country: String,
}
trait Summary {
fn summary(&self) -> String;
}
impl std::fmt::Debug for dyn Summary {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.summary())
}
}
impl Summary for Journal {
fn summary(&self) -> String {
format!("{}\t{}\t{}", self.author, self.year, self.from)
}
}
impl Summary for Conference {
fn summary(&self) -> String {
format!("{}\t{}\t{}", self.author, self.year, self.country)
}
}
// can't defer concrete type in compile stage, trait constrain can't used here
// fn init_default(_type: &str) -> impl Summary {
// if _type == "journal" {
// Journal {
// author: "hjd".to_owned(),
// year: 2018,
// from: "Nature".to_owned(),
// }
// } else {
// Conference {
// author: "hjd".to_owned(),
// year: 2018,
// country: "China".to_owned(),
// }
// }
// }
// 只能使用特征对象进行动态分发,因为返回类型编译期无法推理获知
fn init_default(_type: &str) -> Box<dyn Summary> {
if _type == "journal" {
Box::new(Journal {
author: "hjd".to_owned(),
year: 2018,
from: "Nature".to_owned(),
})
} else {
Box::new(Conference {
author: "hjd".to_owned(),
year: 2018,
country: "China".to_owned(),
})
}
}
fn main() {
let p1 = init_default("journal");
let p2 = init_default("conference");
let p3 = init_default("journal");
let p4 = init_default("conference");
let p_list = vec![p1, p2, p3, p4];
// dyn Summary是一个特征对象类型,它忘记了自己之前的具体类型,只能调用Summary特征中的方法
for p in p_list.iter() {
println!("{:?}", p);
}
}