public
Last active

Dynamic typing in Rust

  • Download Gist
dynamic_typing.rs
Rust
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
use std::unstable::intrinsics::{TyDesc, get_tydesc, forget};
use std::util::Void;
use std::cast::transmute;
 
///////////////////////////////////////////////////////////////////////////////
// TypeId
///////////////////////////////////////////////////////////////////////////////
 
/// `TypeId` represents a globally unique identifier for a type
pub struct TypeId {
priv t: *TyDesc
}
 
impl TypeId {
pub fn of<T>() -> TypeId {
TypeId{ t: unsafe { get_tydesc::<T>() } }
}
}
 
impl Eq for TypeId {
fn eq(&self, &other: &TypeId) -> bool {
self.t == other.t
}
}
 
///////////////////////////////////////////////////////////////////////////////
// Any trait
///////////////////////////////////////////////////////////////////////////////
 
/// The `Any` trait is implemented by all types, and can be used for dynamic typing
pub trait Any {
fn get_type_id(&self) -> TypeId {
TypeId::of::<Self>()
}
fn as_void_ptr(&self) -> *Void {
self as *Self as *Void
}
}
impl<T> Any for T {}
 
/// Extension methods for a borrowed `Any` trait object
trait AnyRefExt {
/// Returns true if the boxed type is the same as `T`
fn is<T>(&self) -> bool;
 
/// Returns an reference to the boxed value if it is of type `T`, or
/// `None` if it isn't.
fn opt_ref<'a, T>(&'a self) -> Option<&'a T> ;
}
 
impl<'self> AnyRefExt for &'self Any {
fn is<T>(&self) -> bool {
// Get TypeId of the type this function is instantiated with
let t = TypeId::of::<T>();
 
// Get TypeId of the type in the trait object
let boxed = self.get_type_id();
 
// Compare both TypeIds on equality
t == boxed
}
 
fn opt_ref<'a, T>(&'a self) -> Option<&'a T> {
if self.is::<T>() {
// Extract the pointer to the boxed value
let boxed_ptr: &'a T = unsafe {
transmute(self.as_void_ptr())
};
 
Some(boxed_ptr)
} else {
None
}
}
}
 
/// Extension methods for a owning `Any` trait object
trait AnyOwnExt {
/// Returns the boxed value if it is of type `T`, or
/// `None` if it isn't.
fn move<'a, T>(self) -> Option<~T>;
}
 
impl AnyOwnExt for ~Any {
fn move<'a, T>(self) -> Option<~T> {
if { let tmp: &Any = self; tmp.is::<T>() } {
unsafe {
// Extract the pointer to the boxed value, temporary alias with self
let boxed_ptr: ~T = transmute(self.as_void_ptr());
 
// Prevent destructor on self being run
forget(self);
 
Some(boxed_ptr)
}
} else {
None
}
}
}
 
///////////////////////////////////////////////////////////////////////////////
// Testing
///////////////////////////////////////////////////////////////////////////////
 
fn identify(a: &Any) {
if a.is::<uint>() {
let v: uint = *a.opt_ref().unwrap();
println!("{}: uint", v);
} else if a.is::<int>() {
let v: int = *a.opt_ref().unwrap();
println!("{}: int", v);
} else {
println("unhandled type!")
}
}
 
fn main() {
let a = ~1u as ~Any;
let b = @2i as @Any;
let c = &3u8 as &Any;
 
identify(a);
identify(b);
identify(c);
 
let v: ~uint = a.move().unwrap();
assert_eq!(v, ~1);
}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.