In Rust, a &T
where T
is a trait
is a fat reference, which actually corresponds to raw::TraitObject
:
pub struct TraitObject {
pub data: *mut (),
pub vtable: *mut (),
}
Using TraitObject
, one can de-construct and re-construct a &T
at leisure.
However, while obtaining the vtable
from de-constructing a &T
is easy, what if I never have the &T
in the first place, but just a T
and S
; essentially, something along the lines of:
fn make_vptr<T: ?Sized, S>() -> *mut ();
How could I divine the v-ptr from there? Is there any intrinsic I could use?
Note: the naive implementation of creating a S
(or conjuring it from thin-air) and then making a &T
reference does not work; the compiler complains that T
is not necessarily a trait
and therefore that &T
is either one pointer or two pointers in size.
A possibility is to use a macro to do the magic job:
#![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
})
);
This code will not compile if T
is not a trait (thanks to transmute(..)
checking that &T
is a fat pointer) or if T
is not implemented by S
(thanks to the assignment).
Then, it can be used directly:
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);
}