Skip to content

Instantly share code, notes, and snippets.

@ProgramCrafter
Created July 18, 2022 03:00
Show Gist options
  • Save ProgramCrafter/621a8142a71f882cebfa777634976a40 to your computer and use it in GitHub Desktop.
Save ProgramCrafter/621a8142a71f882cebfa777634976a40 to your computer and use it in GitHub Desktop.
Rust vtables
#![feature(bench_black_box)]
use std::ptr::{read, write};
use std::hint::black_box;
use std::mem::size_of;
use std::any::Any;
trait Animal: Any {
fn say(&self) -> &'static str;
fn name(&self) -> &'static str;
}
struct Cat;
impl Animal for Cat {
fn say(&self) -> &'static str {"meow"}
fn name(&self) -> &'static str {"cat"}
}
struct Dog;
impl Animal for Dog {
fn say(&self) -> &'static str {"bark"}
fn name(&self) -> &'static str {"dog"}
}
fn inspect_fn_ptr(f: usize) -> &'static str {
if f == Cat::type_id as usize {return "Cat::type_id";}
if f == Dog::type_id as usize {return "Dog::type_id";}
if f == Cat::say as usize {return "Cat::say";}
if f == Cat::name as usize {return "Cat::name";}
if f == Dog::say as usize {return "Dog::say";}
if f == Dog::name as usize {return "Dog::name";}
"?"
}
unsafe fn inspect_vtable(reference: &dyn Animal) -> usize {
let pr = &reference as *const &dyn Animal as *const usize;
println!();
println!("Inspecting metadata of animal {}", reference.name());
println!("Size of reference: {}", size_of::<&dyn Animal>());
const PTR_SIZE: usize = size_of::<usize>();
let meta = read(pr.offset(1));
println!("First {PTR_SIZE} bytes are {}", read(pr));
println!("Vtable {PTR_SIZE} bytes are {}", meta);
println!("Cat::say = {}", <Cat as Animal>::say as usize);
println!("Dog::say = {}", <Dog as Animal>::say as usize);
let pm = meta as *const usize;
println!("Vtable[0] = {} [{}::drop_in_place?]", read(pm), reference.name());
println!("Vtable[1] = {} [size?]", read(pm.offset(1)));
println!("Vtable[2] = {} [alignment?]", read(pm.offset(2)));
println!("Vtable[3] = {} [{}]", read(pm.offset(3)), inspect_fn_ptr(read(pm.offset(3))));
println!("Vtable[4] = {} [{}]", read(pm.offset(4)), inspect_fn_ptr(read(pm.offset(4))));
println!("Vtable[5] = {} [{}]", read(pm.offset(5)), inspect_fn_ptr(read(pm.offset(5))));
println!();
meta
}
unsafe fn change_vtable(reference: &mut &dyn Animal, meta: usize) {
let pr = reference as *mut &dyn Animal as *mut usize;
write(pr.offset(1), meta);
}
fn main() {
let cat = Cat;
// black boxing just because reference becomes invalid later
// and I don't need optimizations inlining Cat at line 83
let mut animal: &dyn Animal = black_box(&cat);
unsafe {inspect_vtable(animal)};
println!("{}", animal.say());
unsafe {change_vtable(&mut animal, inspect_vtable(&Dog))};
println!("{}", animal.say());
}
Inspecting metadata of animal cat
Size of reference: 16
First 8 bytes are 140724672137136
Vtable 8 bytes are 94504553959576
Cat::say = 94504553671552
Dog::say = 94504553671584
Vtable[0] = 94504553671536 [cat::drop_in_place?]
Vtable[1] = 0 [size?]
Vtable[2] = 1 [alignment?]
Vtable[3] = 94504553671424 [Cat::type_id]
Vtable[4] = 94504553671552 [Cat::say]
Vtable[5] = 94504553671568 [Cat::name]
meow
Inspecting metadata of animal dog
Size of reference: 16
First 8 bytes are 94504553897984
Vtable 8 bytes are 94504553959656
Cat::say = 94504553671552
Dog::say = 94504553671584
Vtable[0] = 94504553671536 [dog::drop_in_place?]
Vtable[1] = 0 [size?]
Vtable[2] = 1 [alignment?]
Vtable[3] = 94504553671440 [Dog::type_id]
Vtable[4] = 94504553671584 [Dog::say]
Vtable[5] = 94504553671600 [Dog::name]
bark
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment