]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
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 | ||
1a4d82fc JJ |
11 | //! This module implements the `Any` trait, which enables dynamic typing |
12 | //! of any `'static` type through runtime reflection. | |
13 | //! | |
14 | //! `Any` itself can be used to get a `TypeId`, and has more features when used | |
15 | //! as a trait object. As `&Any` (a borrowed trait object), it has the `is` and | |
e9174d1e SL |
16 | //! `downcast_ref` methods, to test if the contained value is of a given type, |
17 | //! and to get a reference to the inner value as a type. As `&mut Any`, there | |
18 | //! is also the `downcast_mut` method, for getting a mutable reference to the | |
9cc50fc6 SL |
19 | //! inner value. `Box<Any>` adds the `downcast` method, which attempts to |
20 | //! convert to a `Box<T>`. See the [`Box`] documentation for the full details. | |
1a4d82fc JJ |
21 | //! |
22 | //! Note that &Any is limited to testing whether a value is of a specified | |
23 | //! concrete type, and cannot be used to test whether a type implements a trait. | |
24 | //! | |
54a0048b | 25 | //! [`Box`]: ../../std/boxed/struct.Box.html |
9cc50fc6 | 26 | //! |
1a4d82fc JJ |
27 | //! # Examples |
28 | //! | |
29 | //! Consider a situation where we want to log out a value passed to a function. | |
85aaf69f | 30 | //! We know the value we're working on implements Debug, but we don't know its |
1a4d82fc JJ |
31 | //! concrete type. We want to give special treatment to certain types: in this |
32 | //! case printing out the length of String values prior to their value. | |
33 | //! We don't know the concrete type of our value at compile time, so we need to | |
34 | //! use runtime reflection instead. | |
35 | //! | |
36 | //! ```rust | |
85aaf69f | 37 | //! use std::fmt::Debug; |
1a4d82fc JJ |
38 | //! use std::any::Any; |
39 | //! | |
85aaf69f SL |
40 | //! // Logger function for any type that implements Debug. |
41 | //! fn log<T: Any + Debug>(value: &T) { | |
1a4d82fc JJ |
42 | //! let value_any = value as &Any; |
43 | //! | |
44 | //! // try to convert our value to a String. If successful, we want to | |
45 | //! // output the String's length as well as its value. If not, it's a | |
46 | //! // different type: just print it out unadorned. | |
47 | //! match value_any.downcast_ref::<String>() { | |
48 | //! Some(as_string) => { | |
49 | //! println!("String ({}): {}", as_string.len(), as_string); | |
50 | //! } | |
51 | //! None => { | |
52 | //! println!("{:?}", value); | |
53 | //! } | |
54 | //! } | |
55 | //! } | |
56 | //! | |
57 | //! // This function wants to log its parameter out prior to doing work with it. | |
c34b1796 | 58 | //! fn do_work<T: Any + Debug>(value: &T) { |
1a4d82fc JJ |
59 | //! log(value); |
60 | //! // ...do some other work | |
61 | //! } | |
62 | //! | |
63 | //! fn main() { | |
64 | //! let my_string = "Hello World".to_string(); | |
65 | //! do_work(&my_string); | |
66 | //! | |
67 | //! let my_i8: i8 = 100; | |
68 | //! do_work(&my_i8); | |
69 | //! } | |
70 | //! ``` | |
71 | ||
85aaf69f | 72 | #![stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc | 73 | |
c34b1796 | 74 | use fmt; |
85aaf69f | 75 | use intrinsics; |
1a4d82fc JJ |
76 | |
77 | /////////////////////////////////////////////////////////////////////////////// | |
78 | // Any trait | |
79 | /////////////////////////////////////////////////////////////////////////////// | |
80 | ||
9346a6ac | 81 | /// A type to emulate dynamic typing. |
1a4d82fc | 82 | /// |
3157f602 | 83 | /// Most types implement `Any`. However, any type which contains a non-`'static` reference does not. |
9346a6ac | 84 | /// See the [module-level documentation][mod] for more details. |
c34b1796 | 85 | /// |
9346a6ac | 86 | /// [mod]: index.html |
85aaf69f | 87 | #[stable(feature = "rust1", since = "1.0.0")] |
9e0c209e | 88 | pub trait Any: 'static { |
9346a6ac | 89 | /// Gets the `TypeId` of `self`. |
5bcae85e SL |
90 | /// |
91 | /// # Examples | |
92 | /// | |
93 | /// ``` | |
94 | /// #![feature(get_type_id)] | |
95 | /// | |
96 | /// use std::any::{Any, TypeId}; | |
97 | /// | |
98 | /// fn is_string(s: &Any) -> bool { | |
99 | /// TypeId::of::<String>() == s.get_type_id() | |
100 | /// } | |
101 | /// | |
102 | /// fn main() { | |
103 | /// assert_eq!(is_string(&0), false); | |
104 | /// assert_eq!(is_string(&"cookie monster".to_owned()), true); | |
105 | /// } | |
106 | /// ``` | |
62682a34 | 107 | #[unstable(feature = "get_type_id", |
e9174d1e SL |
108 | reason = "this method will likely be replaced by an associated static", |
109 | issue = "27745")] | |
1a4d82fc JJ |
110 | fn get_type_id(&self) -> TypeId; |
111 | } | |
112 | ||
92a42be0 | 113 | #[stable(feature = "rust1", since = "1.0.0")] |
9e0c209e | 114 | impl<T: 'static + ?Sized > Any for T { |
1a4d82fc JJ |
115 | fn get_type_id(&self) -> TypeId { TypeId::of::<T>() } |
116 | } | |
117 | ||
118 | /////////////////////////////////////////////////////////////////////////////// | |
119 | // Extension methods for Any trait objects. | |
1a4d82fc JJ |
120 | /////////////////////////////////////////////////////////////////////////////// |
121 | ||
c34b1796 AL |
122 | #[stable(feature = "rust1", since = "1.0.0")] |
123 | impl fmt::Debug for Any { | |
124 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
125 | f.pad("Any") | |
126 | } | |
127 | } | |
128 | ||
9346a6ac AL |
129 | // Ensure that the result of e.g. joining a thread can be printed and |
130 | // hence used with `unwrap`. May eventually no longer be needed if | |
131 | // dispatch works with upcasting. | |
132 | #[stable(feature = "rust1", since = "1.0.0")] | |
133 | impl fmt::Debug for Any + Send { | |
134 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
135 | f.pad("Any") | |
136 | } | |
137 | } | |
138 | ||
1a4d82fc | 139 | impl Any { |
5bcae85e SL |
140 | /// Returns true if the boxed type is the same as `T`. |
141 | /// | |
142 | /// # Examples | |
143 | /// | |
144 | /// ``` | |
145 | /// use std::any::Any; | |
146 | /// | |
147 | /// fn is_string(s: &Any) { | |
148 | /// if s.is::<String>() { | |
149 | /// println!("It's a string!"); | |
150 | /// } else { | |
151 | /// println!("Not a string..."); | |
152 | /// } | |
153 | /// } | |
154 | /// | |
155 | /// fn main() { | |
156 | /// is_string(&0); | |
157 | /// is_string(&"cookie monster".to_owned()); | |
158 | /// } | |
159 | /// ``` | |
85aaf69f | 160 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc | 161 | #[inline] |
c34b1796 | 162 | pub fn is<T: Any>(&self) -> bool { |
1a4d82fc JJ |
163 | // Get TypeId of the type this function is instantiated with |
164 | let t = TypeId::of::<T>(); | |
165 | ||
166 | // Get TypeId of the type in the trait object | |
167 | let boxed = self.get_type_id(); | |
168 | ||
169 | // Compare both TypeIds on equality | |
170 | t == boxed | |
171 | } | |
172 | ||
173 | /// Returns some reference to the boxed value if it is of type `T`, or | |
174 | /// `None` if it isn't. | |
5bcae85e SL |
175 | /// |
176 | /// # Examples | |
177 | /// | |
178 | /// ``` | |
179 | /// use std::any::Any; | |
180 | /// | |
181 | /// fn print_if_string(s: &Any) { | |
182 | /// if let Some(string) = s.downcast_ref::<String>() { | |
183 | /// println!("It's a string({}): '{}'", string.len(), string); | |
184 | /// } else { | |
185 | /// println!("Not a string..."); | |
186 | /// } | |
187 | /// } | |
188 | /// | |
189 | /// fn main() { | |
190 | /// print_if_string(&0); | |
191 | /// print_if_string(&"cookie monster".to_owned()); | |
192 | /// } | |
193 | /// ``` | |
85aaf69f | 194 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc | 195 | #[inline] |
c34b1796 | 196 | pub fn downcast_ref<T: Any>(&self) -> Option<&T> { |
1a4d82fc JJ |
197 | if self.is::<T>() { |
198 | unsafe { | |
9e0c209e | 199 | Some(&*(self as *const Any as *const T)) |
1a4d82fc JJ |
200 | } |
201 | } else { | |
202 | None | |
203 | } | |
204 | } | |
205 | ||
206 | /// Returns some mutable reference to the boxed value if it is of type `T`, or | |
207 | /// `None` if it isn't. | |
5bcae85e SL |
208 | /// |
209 | /// # Examples | |
210 | /// | |
211 | /// ``` | |
212 | /// use std::any::Any; | |
213 | /// | |
214 | /// fn modify_if_u32(s: &mut Any) { | |
215 | /// if let Some(num) = s.downcast_mut::<u32>() { | |
216 | /// *num = 42; | |
217 | /// } | |
218 | /// } | |
219 | /// | |
220 | /// fn main() { | |
221 | /// let mut x = 10u32; | |
222 | /// let mut s = "starlord".to_owned(); | |
223 | /// | |
224 | /// modify_if_u32(&mut x); | |
225 | /// modify_if_u32(&mut s); | |
226 | /// | |
227 | /// assert_eq!(x, 42); | |
228 | /// assert_eq!(&s, "starlord"); | |
229 | /// } | |
230 | /// ``` | |
85aaf69f | 231 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc | 232 | #[inline] |
c34b1796 | 233 | pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> { |
1a4d82fc JJ |
234 | if self.is::<T>() { |
235 | unsafe { | |
9e0c209e | 236 | Some(&mut *(self as *mut Any as *mut T)) |
1a4d82fc JJ |
237 | } |
238 | } else { | |
239 | None | |
240 | } | |
241 | } | |
242 | } | |
85aaf69f | 243 | |
c34b1796 AL |
244 | impl Any+Send { |
245 | /// Forwards to the method defined on the type `Any`. | |
5bcae85e SL |
246 | /// |
247 | /// # Examples | |
248 | /// | |
249 | /// ``` | |
250 | /// use std::any::Any; | |
251 | /// | |
252 | /// fn is_string(s: &(Any + Send)) { | |
253 | /// if s.is::<String>() { | |
254 | /// println!("It's a string!"); | |
255 | /// } else { | |
256 | /// println!("Not a string..."); | |
257 | /// } | |
258 | /// } | |
259 | /// | |
260 | /// fn main() { | |
261 | /// is_string(&0); | |
262 | /// is_string(&"cookie monster".to_owned()); | |
263 | /// } | |
264 | /// ``` | |
c34b1796 AL |
265 | #[stable(feature = "rust1", since = "1.0.0")] |
266 | #[inline] | |
267 | pub fn is<T: Any>(&self) -> bool { | |
268 | Any::is::<T>(self) | |
269 | } | |
270 | ||
271 | /// Forwards to the method defined on the type `Any`. | |
5bcae85e SL |
272 | /// |
273 | /// # Examples | |
274 | /// | |
275 | /// ``` | |
276 | /// use std::any::Any; | |
277 | /// | |
278 | /// fn print_if_string(s: &(Any + Send)) { | |
279 | /// if let Some(string) = s.downcast_ref::<String>() { | |
280 | /// println!("It's a string({}): '{}'", string.len(), string); | |
281 | /// } else { | |
282 | /// println!("Not a string..."); | |
283 | /// } | |
284 | /// } | |
285 | /// | |
286 | /// fn main() { | |
287 | /// print_if_string(&0); | |
288 | /// print_if_string(&"cookie monster".to_owned()); | |
289 | /// } | |
290 | /// ``` | |
c34b1796 AL |
291 | #[stable(feature = "rust1", since = "1.0.0")] |
292 | #[inline] | |
293 | pub fn downcast_ref<T: Any>(&self) -> Option<&T> { | |
294 | Any::downcast_ref::<T>(self) | |
295 | } | |
296 | ||
297 | /// Forwards to the method defined on the type `Any`. | |
5bcae85e SL |
298 | /// |
299 | /// # Examples | |
300 | /// | |
301 | /// ``` | |
302 | /// use std::any::Any; | |
303 | /// | |
304 | /// fn modify_if_u32(s: &mut (Any+ Send)) { | |
305 | /// if let Some(num) = s.downcast_mut::<u32>() { | |
306 | /// *num = 42; | |
307 | /// } | |
308 | /// } | |
309 | /// | |
310 | /// fn main() { | |
311 | /// let mut x = 10u32; | |
312 | /// let mut s = "starlord".to_owned(); | |
313 | /// | |
314 | /// modify_if_u32(&mut x); | |
315 | /// modify_if_u32(&mut s); | |
316 | /// | |
317 | /// assert_eq!(x, 42); | |
318 | /// assert_eq!(&s, "starlord"); | |
319 | /// } | |
320 | /// ``` | |
c34b1796 AL |
321 | #[stable(feature = "rust1", since = "1.0.0")] |
322 | #[inline] | |
323 | pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> { | |
324 | Any::downcast_mut::<T>(self) | |
325 | } | |
326 | } | |
327 | ||
328 | ||
85aaf69f SL |
329 | /////////////////////////////////////////////////////////////////////////////// |
330 | // TypeID and its methods | |
331 | /////////////////////////////////////////////////////////////////////////////// | |
332 | ||
333 | /// A `TypeId` represents a globally unique identifier for a type. | |
334 | /// | |
335 | /// Each `TypeId` is an opaque object which does not allow inspection of what's | |
336 | /// inside but does allow basic operations such as cloning, comparison, | |
337 | /// printing, and showing. | |
338 | /// | |
339 | /// A `TypeId` is currently only available for types which ascribe to `'static`, | |
340 | /// but this limitation may be removed in the future. | |
341 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] | |
342 | #[stable(feature = "rust1", since = "1.0.0")] | |
343 | pub struct TypeId { | |
344 | t: u64, | |
345 | } | |
346 | ||
347 | impl TypeId { | |
348 | /// Returns the `TypeId` of the type this generic function has been | |
5bcae85e SL |
349 | /// instantiated with. |
350 | /// | |
351 | /// # Examples | |
352 | /// | |
353 | /// ``` | |
5bcae85e SL |
354 | /// use std::any::{Any, TypeId}; |
355 | /// | |
c30ab7b3 SL |
356 | /// fn is_string<T: ?Sized + Any>(_s: &T) -> bool { |
357 | /// TypeId::of::<String>() == TypeId::of::<T>() | |
5bcae85e SL |
358 | /// } |
359 | /// | |
360 | /// fn main() { | |
361 | /// assert_eq!(is_string(&0), false); | |
362 | /// assert_eq!(is_string(&"cookie monster".to_owned()), true); | |
363 | /// } | |
364 | /// ``` | |
c34b1796 | 365 | #[stable(feature = "rust1", since = "1.0.0")] |
9e0c209e | 366 | pub fn of<T: ?Sized + 'static>() -> TypeId { |
85aaf69f SL |
367 | TypeId { |
368 | t: unsafe { intrinsics::type_id::<T>() }, | |
369 | } | |
370 | } | |
371 | } |