Created
May 24, 2020 10:57
-
-
Save cssivision/27070193177ff14f406a8aeb4d61946f to your computer and use it in GitHub Desktop.
trait object/Fat pointers/vtable in Rust
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A reference to a trait object is a fat pointer: (data_ptr, vtable_ptr) | |
trait Test { | |
fn add(&self) -> i32; | |
fn sub(&self) -> i32; | |
fn mul(&self) -> i32; | |
} | |
// This will represent our home-brewed fat pointer to a trait object | |
#[repr(C)] | |
struct FatPointer<'a> { | |
/// A reference is a pointer to an instantiated `Data` instance | |
data: &'a mut Data, | |
/// Since we need to pass in literal values like length and alignment it's | |
/// easiest for us to convert pointers to usize-integers instead of the other way around. | |
vtable: *const usize, | |
} | |
// This is the data in our trait object. It's just two numbers we want to operate on. | |
struct Data { | |
a: i32, | |
b: i32, | |
} | |
// ====== function definitions ====== | |
fn add(s: &Data) -> i32 { | |
s.a + s.b | |
} | |
fn sub(s: &Data) -> i32 { | |
s.a - s.b | |
} | |
fn mul(s: &Data) -> i32 { | |
s.a * s.b | |
} | |
fn main() { | |
let mut data = Data {a: 3, b: 2}; | |
// vtable is like special purpose array of pointer-length types with a fixed | |
// format where the three first values has a special meaning like the | |
// length of the array is encoded in the array itself as the second value. | |
let vtable = vec![ | |
0, // pointer to `Drop` (which we're not implementing here) | |
6, // length of vtable | |
8, // alignment | |
// we need to make sure we add these in the same order as defined in the Trait. | |
add as usize, // function pointer - try changing the order of `add` | |
sub as usize, // function pointer - and `sub` to see what happens | |
mul as usize, // function pointer | |
]; | |
let fat_pointer = FatPointer { data: &mut data, vtable: vtable.as_ptr()}; | |
let test = unsafe { std::mem::transmute::<FatPointer, &dyn Test>(fat_pointer) }; | |
// And voalá, it's now a trait object we can call methods on | |
println!("Add: 3 + 2 = {}", test.add()); | |
println!("Sub: 3 - 2 = {}", test.sub()); | |
println!("Mul: 3 * 2 = {}", test.mul()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment