在 Rust 中,T
是trait
的&T
是一个胖引用,它实际上对应于raw::TraitObject
:
pub struct TraitObject {
pub data: *mut (),
pub vtable: *mut (),
}
使用TraitObject
,人们可以在闲暇时解构和重建&T
。
然而,虽然从解构&T
中获得vtable
很容易,但如果我一开始就没有&T
,而只是一个T
和S
怎么办;本质上,大致如下:
fn make_vptr<T: ?Sized, S>() -> *mut ();
我怎么能从那里占卜 v-ptr?我可以使用任何内在因素吗?
注意:创建S
(或凭空变出它)然后进行&T
引用的幼稚实现不起作用;编译器抱怨T
不一定是trait
,因此&T
的大小要么是一个指针,要么是两个指针。
一种可能性是使用宏来完成神奇的工作:
#![feature(raw)]
macro_rules! make_vptr(
($S:ty, $T:ty) => ({
let s: &$S = unsafe { ::std::mem::uninitialized() };
let t: &$T = s;
let r: ::std::raw::TraitObject = unsafe { ::std::mem::transmute(t) };
r.vtable
})
);
如果T
不是特征(感谢transmute(..)
检查&T
是一个胖指针),或者如果T
不是由S
实现(感谢赋值),则此代码将不会编译。
然后,可以直接使用它:
use std::fmt::Display;
fn main() {
let u32_display_vtable = make_vptr!(u32, Display);
let x = 42u32;
let disp: &Display = unsafe {
::std::mem::transmute(::std::raw::TraitObject {
data: &x as *const _ as *mut _,
vtable: u32_display_vtable,
})
};
println!("{}", disp);
}
我认为这目前是不可能的。
为了使其正常工作,您需要能够将T
泛型参数约束为仅接受特征。 你不能这么做。 因此,它永远不会让你对&T
做任何依赖于它作为一种特征的事情,比如获得 vtable。