基于上一篇Rust基础-关于trait之四-不得不说一下rust fat point
如果Trait之间有继承关系时,vtable 是什么布局呢?
如果看过上一篇,那么这张图应该能够看明白了。
所有的trait这么放,那就没办法 区分fn属于哪个trait了。那么如何upcast呢?
1、upcast
通过用AsBase的trait中转一下的办法。如下代码
trait Human {
fn human_say(&self) {
println!("Human...");
}
}
trait AsHuman {
fn as_Human(&self) -> &dyn Human;
}
impl<T: Human> AsHuman for T {
fn as_Human(&self) -> &dyn Human {
self
}
}
trait Female: AsHuman {
fn human_say(&self) {
println!("Female..");
}
}
trait Male: AsHuman {
fn human_say(&self) {
println!("Male..");
}
}
#[derive(Debug)]
struct Creature;
impl Female for Creature {}
impl Male for Creature {}
impl Human for Creature {}
fn main() {
let s: Creature = Creature;
Human::human_say(&s);
Female::human_say(&s);
Male::human_say(&s);
let Hm: &dyn Human = s.as_Human();
Hm.human_say();
let f: &dyn Female = &s;
f.human_say();
let m:&dyn Male=&s;
m.human_say();
}
输出
AsHuman这个trait完成了 Female和Male这两个trait upcast的道路。 polymorphism?yeah
2、downcast
let‘s code:
use std::any::Any;
trait Human {
fn human_say(&self) {
println!("Human...");
}
fn as_any(&self) -> &dyn Any;
}
trait Female {
fn human_say(&self) {
println!("Female...");
}
fn as_any(&self) -> &dyn Any;
}
struct Creature;
impl Human for Creature {
fn as_any(&self) -> &dyn Any {
self
}
}
impl Female for Creature {
fn as_any(&self) -> &dyn Any {
self
}
}
impl Creature{
fn dance(&self){
println!("let dance!");
}
}
fn main() {
let C = Creature;
let hm: &dyn Human = &C;
hm.human_say();
let downcast_hm = hm.as_any().downcast_ref::<Creature>().unwrap();
Human::human_say(downcast_hm);
Female::human_say(downcast_hm);
downcast_hm.dance();
}
这段代码把hm 这个trait object再次转回Creature这个struct。Rust的Any这个trait提供这个能力。
pub trait Any: 'static {
fn type_id(&self) -> TypeId;
}
//通过 type_id 就能够在运行时判断类型,不同于反射。
通过Any::type_id返回TypeId,再通过downcast_ref转回Creature这个objct。
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
if self.is::<T>() {
unsafe { Some(&*(self as *const dyn Any as *const T)) }
} else {
None
}
}
在类型一致时(if self.is::<T>()),通过 unsafe 代码把 trait object 引用的第一个指针(即 data 指针)转为了指向具体类型的引用。
3、最后讲一下trait的compose
let's code:
trait Human {
fn Human_say(&self) {
println!("Human...");
}
}
trait Boy:Human {
fn Boy_say(&self) {
println!("Boy..");
}
}
trait Wife {
fn Wife_say(&self) {
println!("Wife..");
}
}
trait Family :Boy+Wife {
fn Family_say(&self) {
println!("Family..");
}
}
struct Creature;
impl Human for Creature {}
impl Boy for Creature {}
impl Wife for Creature {}
impl Family for Creature {}
fn Man(Tom: &dyn Family){
Tom.Human_say();
Tom.Boy_say();
Tom.Wife_say();
Tom.Family_say();
}
fn main() {
let s: Creature = Creature;
Man(&s);
}
以上的例子,把顺序的继承关系改为平行的组合关系,相对于downcast和upcast这样的做法更清楚更易维护。但在不同的情况下各有优劣,还是要具体分析的。
相关文章
Rust基础-关于trait之一_DarcyZ_SSM的博客-CSDN博客
Rust基础-关于trait之二_DarcyZ_SSM的博客-CSDN博客
Rust基础-关于trait之三_DarcyZ_SSM的博客-CSDN博客