Dan*_*ila 2 pointers vtable rust
我想了解虚拟表的工作原理并可能在将来使用它。我知道DST指针是胖指针(usize,usize),其中第一个指针是数据,第二个指针是虚拟表。我能够通过指针获取数据,但显然它没有\xe2\x80\x99无法获取指向虚拟表的指针,因为来自https://docs.google.com/presentation/d/1q- c7UAyrUlM-eZyTo1pd8SZ0qwA_wYxmPZVOQkoDmH4/edit#slide=id.p结构体的大小应该由下面代码中的size变量中的指针存储,但事实并非如此。也许我没有正确理解虚拟表是如何存储的?
\n我的代码:
\n#![feature(pointer_byte_offsets)]\n\ntrait MyRule {\n fn some_method1(&self) {}\n fn some_method2(&self) {}\n}\nstruct MyData {\n data: u32,\n}\n\nimpl MyRule for MyData {}\n\nfn main() {\n unsafe {\n let b: Box<dyn MyRule> = Box::new(MyData { data: 5 });\n let fat_ptr = Box::into_raw(b);\n dbg!(*(fat_ptr.cast::<u32>())); // data = 5\n let vtbl_ptr = fat_ptr.byte_add(8).cast::<*mut usize>();\n let destructor_ptr = (*vtbl_ptr).cast::<usize>();\n let size = destructor_ptr.byte_add(8);\n dbg!(size);\n dbg!(*size); // Not size!!!\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n我认为问题在于我没有正确获取指向表本身的指针。那么正确的方法是什么?如何访问表元素?
\n我不确定您是否打算这样做,但您的代码的行为就好像 vtable 指针位于结构旁边的堆上一样。事实上,虚函数表指针位于堆栈上的其他指针旁边。
Box<dyn T>本质上相当于(*mut {data}, *mut {vtable}),但是当您尝试像这样访问 vtable 指针时:
let fat_ptr = Box::into_raw(b);
dbg!(*(fat_ptr.cast::<u32>())); // data = 5
let vtbl_ptr = fat_ptr.byte_add(8).cast::<*mut usize>();
Run Code Online (Sandbox Code Playgroud)
正在byte_add偏移指向堆上结构的MyData数字,而不是移动到堆栈上的其他位置。然后.cast::<*mut usize>()说将该新数字解释为指向a 的指针usize(又名 *mut *mut usize)。
相反,您真正想做的是解构该Box结构以获取这两个指针:
let (pointer, vtable) = mem::transmute_copy::<Box<_>, (*const u8, *const usize)>(&b);
Run Code Online (Sandbox Code Playgroud)
然后你只需使用 useize 偏移 1 即可获得指向 的指针size:
let size_ptr = vtable.add(1); // size is offset by 1 usize
Run Code Online (Sandbox Code Playgroud)
完整示例:
use std::mem;
trait MyRule {
fn some_method1(&self) {}
fn some_method2(&self) {}
}
struct MyData {
data: u32,
}
impl MyRule for MyData {}
fn main() {
unsafe {
let b: Box<dyn MyRule> = Box::new(MyData { data: 5 });
assert_eq!(mem::size_of_val(&b), mem::size_of::<usize>()*2);
// copy pointers from Box
let (pointer, vtable) = mem::transmute_copy::<Box<_>, (*const u8, *const usize)>(&b);
assert_eq!(pointer as usize, &*b as *const _ as *const u8 as usize);
dbg!(pointer, &*b as *const _);
let size_ptr = vtable.add(1); // size is offset by 1 usize
assert_eq!(*size_ptr, mem::size_of::<MyData>());
dbg!(*size_ptr, mem::size_of::<MyData>());
}
}
Run Code Online (Sandbox Code Playgroud)