Last active
January 6, 2023 18:56
-
-
Save Kimundi/6802198 to your computer and use it in GitHub Desktop.
Dynamic typing in Rust
This file contains 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
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