about summary refs log tree commit diff
path: root/src/libcore
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-04-30 20:36:58 -0700
committerAlex Crichton <alex@alexcrichton.com>2014-05-07 08:13:56 -0700
commit8ed728babb057c0f736a63a69ba772e45278148f (patch)
treef36caede39ebc876fe40ef02aaedbffaa0a9f70c /src/libcore
parent85a8e6b80ae91eb00a0bce96a5cec8017e1b37da (diff)
downloadrust-8ed728babb057c0f736a63a69ba772e45278148f.tar.gz
rust-8ed728babb057c0f736a63a69ba772e45278148f.zip
core: Inherit the any module
Diffstat (limited to 'src/libcore')
-rw-r--r--src/libcore/any.rs305
-rw-r--r--src/libcore/lib.rs1
2 files changed, 306 insertions, 0 deletions
diff --git a/src/libcore/any.rs b/src/libcore/any.rs
new file mode 100644
index 00000000000..f22453eb740
--- /dev/null
+++ b/src/libcore/any.rs
@@ -0,0 +1,305 @@
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Traits for dynamic typing of any type (through runtime reflection)
+//!
+//! This module implements the `Any` trait, which enables dynamic typing
+//! of any type, through runtime reflection.
+//!
+//! `Any` itself can be used to get a `TypeId`, and has more features when used as a trait object.
+//! As `&Any` (a borrowed trait object), it has the `is` and `as_ref` methods, to test if the
+//! contained value is of a given type, and to get a reference to the inner value as a type. As
+//! `&mut Any`, there is also the `as_mut` method, for getting a mutable reference to the inner
+//! value. `Box<Any>` adds the `move` method, which will unwrap a `Box<T>` from the object.  See
+//! the extension traits (`*Ext`) for the full details.
+
+use cast::{transmute, transmute_copy};
+use option::{Option, Some, None};
+use owned::Box;
+use raw::TraitObject;
+use result::{Result, Ok, Err};
+use intrinsics::TypeId;
+use intrinsics;
+
+/// A type with no inhabitants
+pub enum Void { }
+
+///////////////////////////////////////////////////////////////////////////////
+// Any trait
+///////////////////////////////////////////////////////////////////////////////
+
+/// The `Any` trait is implemented by all types, and can be used as a trait object
+/// for dynamic typing
+pub trait Any {
+    /// Get the `TypeId` of `self`
+    fn get_type_id(&self) -> TypeId;
+}
+
+impl<T: 'static> Any for T {
+    /// Get the `TypeId` of `self`
+    fn get_type_id(&self) -> TypeId {
+        TypeId::of::<T>()
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Extension methods for Any trait objects.
+// Implemented as three extension traits so that the methods can be generic.
+///////////////////////////////////////////////////////////////////////////////
+
+/// Extension methods for a referenced `Any` trait object
+pub trait AnyRefExt<'a> {
+    /// Returns true if the boxed type is the same as `T`
+    fn is<T: 'static>(self) -> bool;
+
+    /// Returns some reference to the boxed value if it is of type `T`, or
+    /// `None` if it isn't.
+    fn as_ref<T: 'static>(self) -> Option<&'a T>;
+}
+
+impl<'a> AnyRefExt<'a> for &'a Any {
+    #[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>() {
+            unsafe {
+                // Get the raw representation of the trait object
+                let to: TraitObject = transmute_copy(&self);
+
+                // Extract the data pointer
+                Some(transmute(to.data))
+            }
+        } else {
+            None
+        }
+    }
+}
+
+/// Extension methods for a mutable referenced `Any` trait object
+pub trait AnyMutRefExt<'a> {
+    /// Returns some mutable reference to the boxed value if it is of type `T`, or
+    /// `None` if it isn't.
+    fn as_mut<T: 'static>(self) -> Option<&'a mut T>;
+}
+
+impl<'a> AnyMutRefExt<'a> for &'a mut Any {
+    #[inline]
+    fn as_mut<T: 'static>(self) -> Option<&'a mut T> {
+        if self.is::<T>() {
+            unsafe {
+                // Get the raw representation of the trait object
+                let to: TraitObject = transmute_copy(&self);
+
+                // Extract the data pointer
+                Some(transmute(to.data))
+            }
+        } else {
+            None
+        }
+    }
+}
+
+/// Extension methods for an owning `Any` trait object
+pub trait AnyOwnExt {
+    /// Returns the boxed value if it is of type `T`, or
+    /// `Err(Self)` if it isn't.
+    fn move<T: 'static>(self) -> Result<Box<T>, Self>;
+}
+
+impl AnyOwnExt for Box<Any> {
+    #[inline]
+    fn move<T: 'static>(self) -> Result<Box<T>, Box<Any>> {
+        if self.is::<T>() {
+            unsafe {
+                // Get the raw representation of the trait object
+                let to: TraitObject = transmute_copy(&self);
+
+                // Prevent destructor on self being run
+                intrinsics::forget(self);
+
+                // Extract the data pointer
+                Ok(transmute(to.data))
+            }
+        } else {
+            Err(self)
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use prelude::*;
+    use super::*;
+    use owned::Box;
+    use str::StrSlice;
+
+    #[deriving(Eq, Show)]
+    struct Test;
+
+    static TEST: &'static str = "Test";
+
+    #[test]
+    fn any_referenced() {
+        let (a, b, c) = (&5u as &Any, &TEST as &Any, &Test as &Any);
+
+        assert!(a.is::<uint>());
+        assert!(!b.is::<uint>());
+        assert!(!c.is::<uint>());
+
+        assert!(!a.is::<&'static str>());
+        assert!(b.is::<&'static str>());
+        assert!(!c.is::<&'static str>());
+
+        assert!(!a.is::<Test>());
+        assert!(!b.is::<Test>());
+        assert!(c.is::<Test>());
+    }
+
+    #[test]
+    fn any_owning() {
+        let (a, b, c) = (box 5u as Box<Any>, box TEST as Box<Any>, box Test as Box<Any>);
+
+        assert!(a.is::<uint>());
+        assert!(!b.is::<uint>());
+        assert!(!c.is::<uint>());
+
+        assert!(!a.is::<&'static str>());
+        assert!(b.is::<&'static str>());
+        assert!(!c.is::<&'static str>());
+
+        assert!(!a.is::<Test>());
+        assert!(!b.is::<Test>());
+        assert!(c.is::<Test>());
+    }
+
+    #[test]
+    fn any_as_ref() {
+        let a = &5u as &Any;
+
+        match a.as_ref::<uint>() {
+            Some(&5) => {}
+            x => fail!("Unexpected value {:?}", x)
+        }
+
+        match a.as_ref::<Test>() {
+            None => {}
+            x => fail!("Unexpected value {:?}", x)
+        }
+    }
+
+    #[test]
+    fn any_as_mut() {
+        let mut a = 5u;
+        let mut b = box 7u;
+
+        let a_r = &mut a as &mut Any;
+        let tmp: &mut uint = b;
+        let b_r = tmp as &mut Any;
+
+        match a_r.as_mut::<uint>() {
+            Some(x) => {
+                assert_eq!(*x, 5u);
+                *x = 612;
+            }
+            x => fail!("Unexpected value {:?}", x)
+        }
+
+        match b_r.as_mut::<uint>() {
+            Some(x) => {
+                assert_eq!(*x, 7u);
+                *x = 413;
+            }
+            x => fail!("Unexpected value {:?}", x)
+        }
+
+        match a_r.as_mut::<Test>() {
+            None => (),
+            x => fail!("Unexpected value {:?}", x)
+        }
+
+        match b_r.as_mut::<Test>() {
+            None => (),
+            x => fail!("Unexpected value {:?}", x)
+        }
+
+        match a_r.as_mut::<uint>() {
+            Some(&612) => {}
+            x => fail!("Unexpected value {:?}", x)
+        }
+
+        match b_r.as_mut::<uint>() {
+            Some(&413) => {}
+            x => fail!("Unexpected value {:?}", x)
+        }
+    }
+
+    #[test]
+    fn any_move() {
+        let a = box 8u as Box<Any>;
+        let b = box Test as Box<Any>;
+
+        match a.move::<uint>() {
+            Ok(a) => { assert_eq!(a, box 8u); }
+            Err(..) => fail!()
+        }
+        match b.move::<Test>() {
+            Ok(a) => { assert_eq!(a, box Test); }
+            Err(..) => fail!()
+        }
+
+        let a = box 8u as Box<Any>;
+        let b = box Test as Box<Any>;
+
+        assert!(a.move::<Box<Test>>().is_err());
+        assert!(b.move::<Box<uint>>().is_err());
+    }
+
+    #[test]
+    fn test_show() {
+        let a = box 8u as Box<Any>;
+        let b = box Test as Box<Any>;
+        assert_eq!(format!("{}", a), "Box<Any>".to_owned());
+        assert_eq!(format!("{}", b), "Box<Any>".to_owned());
+
+        let a = &8u as &Any;
+        let b = &Test as &Any;
+        assert_eq!(format!("{}", a), "&Any".to_owned());
+        assert_eq!(format!("{}", b), "&Any".to_owned());
+    }
+}
+
+#[cfg(test)]
+mod bench {
+    extern crate test;
+
+    use any::{Any, AnyRefExt};
+    use option::Some;
+    use self::test::Bencher;
+
+    #[bench]
+    fn bench_as_ref(b: &mut Bencher) {
+        b.iter(|| {
+            let mut x = 0; let mut y = &mut x as &mut Any;
+            test::black_box(&mut y);
+            test::black_box(y.as_ref::<int>() == Some(&0));
+        });
+    }
+}
diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs
index 5206ba1f32a..08b0434d827 100644
--- a/src/libcore/lib.rs
+++ b/src/libcore/lib.rs
@@ -37,5 +37,6 @@ pub mod container;
 
 /* Core types and methods on primitives */
 
+pub mod any;
 pub mod finally;
 pub mod char;