Skip to content

Instantly share code, notes, and snippets.

@bvssvni
Last active August 29, 2015 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bvssvni/9327294 to your computer and use it in GitHub Desktop.
Save bvssvni/9327294 to your computer and use it in GitHub Desktop.
Emulating dynamic types: How to implement Any for owned Trait pointers
//!
//! >rustc --version
//! rustc 0.10-pre (ee8f45e 2014-02-18 13:41:49 -0800)
//!
//! In this example we will implement Any for a trait pointer.
//! This is useful when we want to design an Entity/Component system
//! that is extensible beyond the library.
//!
//! As example we have two objects 'Player' and 'Enemy'.
//! Both implement the trait 'Entity' which is used 90% of the time.
//! In a few cases we want to access type specific methods.
//! The Any trait can be used to cast dynamically from
//! Entity to Player or Enemey with the method 'as_ref'.
//!
//! This example is somewhat complicated because
//! the Any trait is not implemented on trait pointers.
//! We can't cast ~Entity to Any.
//! We need to make Entity inherit from Any and implement some extra traits.
//! This requires some copy and pasting from the standard library.
//!
// Some imports to do unsafe operations.
// This is used by the special traits implemented on Any.
use std::cast::transmute;
use std::unstable::intrinsics;
use std::unstable::intrinsics::TypeId;
/// Specifies how to control a player.
#[deriving(Show)]
pub enum UserInput {
Mouse,
Keyboard,
GamePad,
}
/// Players are controlled by user input.
pub struct Player {
name: ~str,
input: UserInput,
}
/// Enemies are not controlled by user input.
pub struct Enemy {
name: ~str
}
/// Entity inherits from the Any trait.
/// Any is implemented by all types, but not for trait pointers by default.
pub trait Entity : Any {
/// Get the name of the entity.
fn get_name<'a>(&'a self) -> &'a str;
}
/// This code is copied from standard library.
impl AnyOwnExt for ~Entity {
#[inline]
fn move<T: 'static>(self) -> Result<~T, ~Entity> {
if self.is::<T>() {
unsafe {
// Extract the pointer to the boxed value, temporary alias with self
let ptr: ~T = transmute(self.as_void_ptr());
// Prevent destructor on self being run
intrinsics::forget(self);
Ok(ptr)
}
} else {
Err(self)
}
}
}
/// This code is copied from standard library.
impl<'a> AnyRefExt<'a> for &'a Entity {
#[inline]
fn is<T: 'static>(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
}
#[inline]
fn as_ref<T: 'static>(self) -> Option<&'a T> {
if self.is::<T>() {
Some(unsafe { transmute(self.as_void_ptr()) })
} else {
None
}
}
}
/// This code is copied from standard library.
impl<'a> AnyMutRefExt<'a> for &'a mut Entity {
#[inline]
fn as_mut<T: 'static>(self) -> Option<&'a mut T> {
if self.is::<T>() {
Some(unsafe { transmute(self.as_mut_void_ptr()) })
} else {
None
}
}
}
impl Entity for Player {
fn get_name<'a>(&'a self) -> &'a str {
self.name.as_slice()
}
}
impl Entity for Enemy {
fn get_name<'a>(&'a self) -> &'a str {
self.name.as_slice()
}
}
pub enum EntityType {
PlayerType(UserInput),
EnemyType,
}
/// Creates new entities.
fn create_entity(name: ~str, entity_type: EntityType) -> ~Entity {
match entity_type {
PlayerType(input) =>
~Player {
name: name, input: input
} as ~Entity,
EnemyType => ~Enemy { name: name } as ~Entity,
}
}
fn main() {
// We build a list of entities.
let mut entities: ~[~Entity] = ~[];
entities.push(create_entity(~"Player1", PlayerType(Keyboard)));
entities.push(create_entity(~"Player2", PlayerType(GamePad)));
entities.push(create_entity(~"Boss", EnemyType));
// Cast one of the entities back to Player.
match entities[1].as_ref::<Player>() {
// Prints out 'GamePad'.
Some(player) => println!("{}", player.input),
None => {}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment