Skip to content

Instantly share code, notes, and snippets.

@Kimundi
Last active January 6, 2023 18:56
Show Gist options
  • Save Kimundi/6802198 to your computer and use it in GitHub Desktop.
Save Kimundi/6802198 to your computer and use it in GitHub Desktop.
Dynamic typing in Rust
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);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment