Skip to content

Instantly share code, notes, and snippets.

@eddyb

eddyb/many.rs Secret

Last active July 31, 2017 18:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eddyb/f828f8ce819a5f00d42d to your computer and use it in GitHub Desktop.
Save eddyb/f828f8ce819a5f00d42d to your computer and use it in GitHub Desktop.
use std::ops::{Add, Sub, Mul};
use std::intrinsics::TypeId;
use std::mem::{size_of, transmute};
use std::raw::TraitObject;
trait Many: 'static {
// HACK(eddyb) Missing upcast to Any to make this clean.
fn get_type_id(&self) -> TypeId { TypeId::of::<&'static Self>() }
fn get_vtable_for_trait(&self, _trait_id: TypeId) -> Option<&'static ()> { None }
}
macro_rules! many {
($($Trait:ty),+ for $ty:ty) => {impl Many for $ty {
fn get_vtable_for_trait(&self, trait_id: TypeId) -> Option<&'static ()> {
$(if trait_id == TypeId::of::<&'static $Trait>() {
Some(unsafe {&*transmute::<&$Trait, TraitObject>(self).vtable})
})else+ else {
None
}
}
}}
}
trait ManyRef<'a> {
fn as_ref<T:'static+?Sized>(self) -> Option<&'a T>;
}
// TODO this should be implementable on Many itself.
impl<'a> ManyRef<'a> for &'a (Many+'static) {
fn as_ref<T:'static+?Sized>(self) -> Option<&'a T> {
let type_id = TypeId::of::<&'static T>();
unsafe {
let obj = transmute::<_, TraitObject>(self);
if size_of::<*const T>() == size_of::<*const ()>() {
if self.get_type_id() == type_id {
Some(*transmute::<_, &&T>(&obj.data))
} else {
None
}
} else {
self.get_vtable_for_trait(type_id).map(|vtable| {
*transmute::<_, &&T>(&TraitObject {
data: obj.data,
vtable: vtable as *const _ as *mut _
})
})
}
}
}
}
many!(Add<u8, Output=u8>, Sub<u8, Output=u8>, Mul<u8, Output=u8> for u8);
trait Foo {}
impl Foo for u8 {}
fn pieces<T>(obj: T) -> (*const (), *const ()) {
unsafe {*transmute::<_, &_>(&obj)}
}
fn main() {
let x = 3u8;
let m = &x as &Many;
println!("{}\n{} {}\n{} {}\n{} {}\n{}",
m.as_ref::<u8>(),
m.as_ref::<Add<u8, Output=u8>>().map(pieces),
m.as_ref::<Add<u8, Output=u8>>().unwrap().add(&5),
m.as_ref::<Sub<u8, Output=u8>>().map(pieces),
m.as_ref::<Sub<u8, Output=u8>>().unwrap().sub(&2),
m.as_ref::<Mul<u8, Output=u8>>().map(pieces),
m.as_ref::<Mul<u8, Output=u8>>().unwrap().mul(&3),
m.as_ref::<Foo>().map(pieces));
assert!(
m.as_ref::<u8>() == Some(&3) &&
m.as_ref::<Add<u8, Output=u8>>().is_some() &&
m.as_ref::<Add<u8, Output=u8>>().unwrap().add(&5) == 8 &&
m.as_ref::<Sub<u8, Output=u8>>().is_some() &&
m.as_ref::<Sub<u8, Output=u8>>().unwrap().sub(&2) == 1 &&
m.as_ref::<Mul<u8, Output=u8>>().is_some() &&
m.as_ref::<Mul<u8, Output=u8>>().unwrap().mul(&3) == 9 &&
m.as_ref::<Foo>().is_none());
}
@dobkeratops
Copy link

is this something else that could be done without unsafe blocks if you had a safe intrinsic for getting trait-obj vtables and composing a trait-obj fatpointer . i really like that idea, it seems there are many possibilities, (including making it easier to describe c++ style classes)

@eddyb
Copy link
Author

eddyb commented Sep 10, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment