]> git.lizzy.rs Git - rust.git/blob - src/libcore/any.rs
2657cd534837231af948f40c086ab4a1da3f52b1
[rust.git] / src / libcore / any.rs
1 // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Traits for dynamic typing of any `'static` type (through runtime reflection)
12 //!
13 //! This module implements the `Any` trait, which enables dynamic typing
14 //! of any `'static` type through runtime reflection.
15 //!
16 //! `Any` itself can be used to get a `TypeId`, and has more features when used as a trait object.
17 //! As `&Any` (a borrowed trait object), it has the `is` and `as_ref` methods, to test if the
18 //! contained value is of a given type, and to get a reference to the inner value as a type. As
19 //! `&mut Any`, there is also the `as_mut` method, for getting a mutable reference to the inner
20 //! value. `Box<Any>` adds the `move` method, which will unwrap a `Box<T>` from the object.  See
21 //! the extension traits (`*Ext`) for the full details.
22
23 use mem::{transmute, transmute_copy};
24 use option::{Option, Some, None};
25 use raw::TraitObject;
26 use intrinsics::TypeId;
27
28 /// A type with no inhabitants
29 pub enum Void { }
30
31 ///////////////////////////////////////////////////////////////////////////////
32 // Any trait
33 ///////////////////////////////////////////////////////////////////////////////
34
35 /// The `Any` trait is implemented by all `'static` types, and can be used for dynamic typing
36 ///
37 /// Every type with no non-`'static` references implements `Any`, so `Any` can be used as a trait
38 /// object to emulate the effects dynamic typing.
39 pub trait Any {
40     /// Get the `TypeId` of `self`
41     fn get_type_id(&self) -> TypeId;
42 }
43
44 impl<T: 'static> Any for T {
45     /// Get the `TypeId` of `self`
46     fn get_type_id(&self) -> TypeId {
47         TypeId::of::<T>()
48     }
49 }
50
51 ///////////////////////////////////////////////////////////////////////////////
52 // Extension methods for Any trait objects.
53 // Implemented as three extension traits so that the methods can be generic.
54 ///////////////////////////////////////////////////////////////////////////////
55
56 /// Extension methods for a referenced `Any` trait object
57 pub trait AnyRefExt<'a> {
58     /// Returns true if the boxed type is the same as `T`
59     fn is<T: 'static>(self) -> bool;
60
61     /// Returns some reference to the boxed value if it is of type `T`, or
62     /// `None` if it isn't.
63     fn as_ref<T: 'static>(self) -> Option<&'a T>;
64 }
65
66 impl<'a> AnyRefExt<'a> for &'a Any {
67     #[inline]
68     fn is<T: 'static>(self) -> bool {
69         // Get TypeId of the type this function is instantiated with
70         let t = TypeId::of::<T>();
71
72         // Get TypeId of the type in the trait object
73         let boxed = self.get_type_id();
74
75         // Compare both TypeIds on equality
76         t == boxed
77     }
78
79     #[inline]
80     fn as_ref<T: 'static>(self) -> Option<&'a T> {
81         if self.is::<T>() {
82             unsafe {
83                 // Get the raw representation of the trait object
84                 let to: TraitObject = transmute_copy(&self);
85
86                 // Extract the data pointer
87                 Some(transmute(to.data))
88             }
89         } else {
90             None
91         }
92     }
93 }
94
95 /// Extension methods for a mutable referenced `Any` trait object
96 pub trait AnyMutRefExt<'a> {
97     /// Returns some mutable reference to the boxed value if it is of type `T`, or
98     /// `None` if it isn't.
99     fn as_mut<T: 'static>(self) -> Option<&'a mut T>;
100 }
101
102 impl<'a> AnyMutRefExt<'a> for &'a mut Any {
103     #[inline]
104     fn as_mut<T: 'static>(self) -> Option<&'a mut T> {
105         if self.is::<T>() {
106             unsafe {
107                 // Get the raw representation of the trait object
108                 let to: TraitObject = transmute_copy(&self);
109
110                 // Extract the data pointer
111                 Some(transmute(to.data))
112             }
113         } else {
114             None
115         }
116     }
117 }
118
119 #[cfg(test)]
120 mod tests {
121     use prelude::*;
122     use super::*;
123     use realstd::owned::{Box, AnyOwnExt};
124     use realstd::str::Str;
125
126     #[deriving(PartialEq, Show)]
127     struct Test;
128
129     static TEST: &'static str = "Test";
130
131     #[test]
132     fn any_referenced() {
133         let (a, b, c) = (&5u as &Any, &TEST as &Any, &Test as &Any);
134
135         assert!(a.is::<uint>());
136         assert!(!b.is::<uint>());
137         assert!(!c.is::<uint>());
138
139         assert!(!a.is::<&'static str>());
140         assert!(b.is::<&'static str>());
141         assert!(!c.is::<&'static str>());
142
143         assert!(!a.is::<Test>());
144         assert!(!b.is::<Test>());
145         assert!(c.is::<Test>());
146     }
147
148     #[test]
149     fn any_owning() {
150         let (a, b, c) = (box 5u as Box<Any>, box TEST as Box<Any>, box Test as Box<Any>);
151
152         assert!(a.is::<uint>());
153         assert!(!b.is::<uint>());
154         assert!(!c.is::<uint>());
155
156         assert!(!a.is::<&'static str>());
157         assert!(b.is::<&'static str>());
158         assert!(!c.is::<&'static str>());
159
160         assert!(!a.is::<Test>());
161         assert!(!b.is::<Test>());
162         assert!(c.is::<Test>());
163     }
164
165     #[test]
166     fn any_as_ref() {
167         let a = &5u as &Any;
168
169         match a.as_ref::<uint>() {
170             Some(&5) => {}
171             x => fail!("Unexpected value {}", x)
172         }
173
174         match a.as_ref::<Test>() {
175             None => {}
176             x => fail!("Unexpected value {}", x)
177         }
178     }
179
180     #[test]
181     fn any_as_mut() {
182         let mut a = 5u;
183         let mut b = box 7u;
184
185         let a_r = &mut a as &mut Any;
186         let tmp: &mut uint = b;
187         let b_r = tmp as &mut Any;
188
189         match a_r.as_mut::<uint>() {
190             Some(x) => {
191                 assert_eq!(*x, 5u);
192                 *x = 612;
193             }
194             x => fail!("Unexpected value {}", x)
195         }
196
197         match b_r.as_mut::<uint>() {
198             Some(x) => {
199                 assert_eq!(*x, 7u);
200                 *x = 413;
201             }
202             x => fail!("Unexpected value {}", x)
203         }
204
205         match a_r.as_mut::<Test>() {
206             None => (),
207             x => fail!("Unexpected value {}", x)
208         }
209
210         match b_r.as_mut::<Test>() {
211             None => (),
212             x => fail!("Unexpected value {}", x)
213         }
214
215         match a_r.as_mut::<uint>() {
216             Some(&612) => {}
217             x => fail!("Unexpected value {}", x)
218         }
219
220         match b_r.as_mut::<uint>() {
221             Some(&413) => {}
222             x => fail!("Unexpected value {}", x)
223         }
224     }
225
226     #[test]
227     fn any_move() {
228         use realstd::any::Any;
229         use realstd::result::{Ok, Err};
230         let a = box 8u as Box<Any>;
231         let b = box Test as Box<Any>;
232
233         match a.move::<uint>() {
234             Ok(a) => { assert!(a == box 8u); }
235             Err(..) => fail!()
236         }
237         match b.move::<Test>() {
238             Ok(a) => { assert!(a == box Test); }
239             Err(..) => fail!()
240         }
241
242         let a = box 8u as Box<Any>;
243         let b = box Test as Box<Any>;
244
245         assert!(a.move::<Box<Test>>().is_err());
246         assert!(b.move::<Box<uint>>().is_err());
247     }
248
249     #[test]
250     fn test_show() {
251         use realstd::to_str::ToStr;
252         let a = box 8u as Box<::realstd::any::Any>;
253         let b = box Test as Box<::realstd::any::Any>;
254         let a_str = a.to_str();
255         let b_str = b.to_str();
256         assert_eq!(a_str.as_slice(), "Box<Any>");
257         assert_eq!(b_str.as_slice(), "Box<Any>");
258
259         let a = &8u as &Any;
260         let b = &Test as &Any;
261         let s = format!("{}", a);
262         assert_eq!(s.as_slice(), "&Any");
263         let s = format!("{}", b);
264         assert_eq!(s.as_slice(), "&Any");
265     }
266
267     #[test]
268     fn any_fixed_vec() {
269         let test = [0u, ..8];
270         let test = &test as &Any;
271         assert!(test.is::<[uint, ..8]>());
272         assert!(!test.is::<[uint, ..10]>());
273     }
274 }
275
276 #[cfg(test)]
277 mod bench {
278     extern crate test;
279
280     use any::{Any, AnyRefExt};
281     use option::Some;
282     use self::test::Bencher;
283
284     #[bench]
285     fn bench_as_ref(b: &mut Bencher) {
286         b.iter(|| {
287             let mut x = 0; let mut y = &mut x as &mut Any;
288             test::black_box(&mut y);
289             test::black_box(y.as_ref::<int>() == Some(&0));
290         });
291     }
292 }