这张图相信C++的同学非常熟悉了,不就是继承、多态 、虚函数、balabala嘛,是的没错。
那么rust是什么布局呢?
它看起来下下面这样子所谓的fat point
有啥好处呢?
1、可以为已有类型实现 trait(比如 blanket implementations)
2、调用虚表中的函数时,只需要引用一次,而在 C++ 中,需要两次。
以下代码是为了深入了解一下关于fat point的机制,
use std::mem::transmute;
use std::fmt::{Debug, Formatter, Error};
struct Fmtfn<'a, T: 'a>(&'a fn(&T, &mut Formatter) -> Result<(), Error>, &'a T);
impl<'a, T> Debug for Fmtfn<'a, T> {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
println!("self.0: {:?}", self.0 as *const _ as usize);
println!("self.1: {:?}", self.1 as *const _ as usize);
println!("f: {:?}", f as *const _ as usize);
self.0(self.1, f)
}
}
fn main() {
let v: Vec<u64> = vec![1, 2, 3, 4];
let a: &Vec<u64> = &v;
let b: &[u64] = &v;
let c: &dyn Debug = &v;
let (_, vtable) = unsafe { transmute::<_, (usize, usize)>(c) };
let fmt3 = unsafe { &*((vtable as *const fn(&Vec<u64>, &mut Formatter) -> Result<(), Error>).offset(3)) };
let fmt0= unsafe { &*((vtable as *const fn(&Vec<u64>, &mut Formatter) -> Result<(), Error>).offset(0)) };
let fmt1= unsafe { &*((vtable as *const fn(&Vec<u64>, &mut Formatter) -> Result<(), Error>).offset(1)) };
let fmt2= unsafe { &*((vtable as *const fn(&Vec<u64>, &mut Formatter) -> Result<(), Error>).offset(2)) };
println!("a: {}", a as *const _ as usize);
println!("b: {:?}", unsafe { transmute::<_, (usize, usize)>(b) });
println!("c: {:?}", unsafe { transmute::<_, (usize, usize)>(c) });
println!("fmt0: {}", fmt0 as *const _ as usize);
println!("fmt1: {}", fmt1 as *const _ as usize);
println!("fmt2: {}", fmt2 as *const _ as usize);
println!("fmt3: {}", fmt3 as *const _ as usize);
println!("{:?}", unsafe { &*(vtable as *const [usize; 3]) });
println!("{:?}", Fmtfn(fmt3, &v));
}
输出:
a和c的第一个指针的地址是一样的,而b不一样,
那么断点看一下:
显而易见,ptr指向的地址就是dataptr,指向了数组的第一个对象,而b的地址很显然是Vec转换为切片后的地址,因为当let b: &[u64] = &v;时会调用Vec的Deref trait(rust/src/liballoc/vec.rs)如下图所示:
那么dataptr有了。接下去找vtable里面有什么。
接下来看输出:
c的第二个地址就是vtable的入口地址和fmt0一样,后面fmt0,fmt1,fmt2,fmt3,每个len是8字节。
似乎vtable里应该只有fn fmt()这个函数,其他是啥?
先看断点的截图:
很明显fmt0是指向的core::ptr::drop_in_place,fmt3是指向的fmt,为什么呢?答案在rust/src/librustc_codegen_llvm/meth.rs里,下图
所以在任何方法之前的 vtable 中就有三个值(大小usize
),而第四个才是自定义的trait。
所以fmt3和函数内的self.0才是指向fn fmt的指针。下一篇介绍trait 的upcast、downcast和compose了。
相关文章
Rust基础-关于trait之一_DarcyZ_SSM的博客-CSDN博客
Rust基础-关于trait之二_DarcyZ_SSM的博客-CSDN博客
Rust基础-关于trait之三_DarcyZ_SSM的博客-CSDN博客