mopa/lib.rs
1// This is largely taken from the Rust distribution, with only comparatively
2// minor additions and alterations. Therefore, their copyright notice follows:
3//
4// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
5// file at the top-level directory of this distribution and at
6// http://rust-lang.org/COPYRIGHT.
7//
8// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
9// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
10// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
11// option. This file may not be copied, modified, or distributed
12// except according to those terms.
13//
14// I have kept my additions under the same terms (being rather fond of MIT/Apache-2.0 myself).
15
16//! **MOPA: My Own Personal Any.** A macro to implement all the `Any` methods on your own trait.
17//!
18//! You like `Any`—its ability to store any `'static` type as a trait object and then downcast it
19//! back to the original type is very convenient, and in fact you need it for whatever misguided
20//! reason. But it’s not enough. What you *really* want is your own trait object type with `Any`’s
21//! functionality glued onto it. Maybe you have a `Person` trait and you want your people to be
22//! able to do various things, but you also want to be able to conveniently downcast the person to
23//! its original type, right? Alas, you can’t write a type like `Box<Person + Any>` (at present,
24//! anyway). So what do you do instead? Do you give up? No, no! No, no! Enter MOPA.
25//!
26//! > There once was a quite friendly trait
27//! > Called `Person`, with much on its plate.
28//! > “I need to be `Any`
29//! > To downcast to `Benny`—
30//! > But I’m not, so I guess I’ll just wait.”
31//!
32//! A pitiful tale, isn’t it? Especially given that there was a bear chasing it with intent to eat
33//! it. Fortunately now you can *mopafy* `Person` in three simple steps:
34//!
35//! 1. Add the `mopa` crate to your `Cargo.toml` as usual and your crate root like so:
36//!
37//! ```rust,ignore
38//! #[macro_use]
39//! extern crate mopa;
40//! ```
41//!
42//! 2. Make `Any` (`mopa::Any`, not `std::any::Any`) a supertrait of `Person`;
43//!
44//! 3. `mopafy!(Person);`.
45//!
46//! And lo, you can now write `person.is::<Benny>()` and `person.downcast_ref::<Benny>()` and so on
47//! to your heart’s content. Simple, huh?
48//!
49//! Oh, by the way, it was actually the person on the bear’s plate. There wasn’t really anything on
50//! `Person`’s plate after all.
51//!
52//! ```rust
53//! #[macro_use]
54//! extern crate mopa;
55//!
56//! struct Bear {
57//! // This might be a pretty fat bear.
58//! fatness: u16,
59//! }
60//!
61//! impl Bear {
62//! fn eat(&mut self, person: Box<Person>) {
63//! self.fatness = (self.fatness as i16 + person.weight()) as u16;
64//! }
65//! }
66//!
67//! trait Person: mopa::Any {
68//! fn panic(&self);
69//! fn yell(&self) { println!("Argh!"); }
70//! fn sleep(&self);
71//! fn weight(&self) -> i16;
72//! }
73//!
74//! mopafy!(Person);
75//!
76//! struct Benny {
77//! // (Benny is not a superhero. He can’t carry more than 256kg of food at once.)
78//! kilograms_of_food: u8,
79//! }
80//!
81//! impl Person for Benny {
82//! fn panic(&self) { self.yell() }
83//! fn sleep(&self) { /* ... */ }
84//! fn weight(&self) -> i16 {
85//! // Who’s trying to find out? I’m scared!
86//! self.yell();
87//! self.kilograms_of_food as i16 + 60
88//! }
89//! }
90//!
91//! struct Chris;
92//!
93//! impl Chris {
94//! // Normal people wouldn’t be brave enough to hit a bear but Chris might.
95//! fn hit(&self, bear: &mut Bear) {
96//! println!("Chris hits the bear! How brave! (Or maybe stupid?)");
97//! // Meh, boundary conditions, what use are they in examples?
98//! // Chris clearly hits quite hard. Poor bear.
99//! bear.fatness -= 1;
100//! }
101//! }
102//!
103//! impl Person for Chris {
104//! fn panic(&self) { /* ... */ }
105//! fn sleep(&self) { /* ... */ }
106//! fn weight(&self) -> i16 { -5 /* antigravity device! cool! */ }
107//! }
108//!
109//! fn simulate_simulation(person: Box<Person>, bear: &mut Bear) {
110//! if person.is::<Benny>() {
111//! // None of the others do, but Benny knows this particular
112//! // bear by reputation and he’s *really* going to be worried.
113//! person.yell()
114//! }
115//! // If it happens to be Chris, he’ll hit the bear.
116//! person.downcast_ref::<Chris>().map(|chris| chris.hit(bear));
117//! bear.eat(person);
118//! }
119//!
120//! fn main() {
121//! let mut bear = Bear { fatness: 10 };
122//! simulate_simulation(Box::new(Benny { kilograms_of_food: 5 }), &mut bear);
123//! simulate_simulation(Box::new(Chris), &mut bear);
124//! }
125//! ```
126//!
127//! Now *should* you do something like this? Probably not. Enums are probably a better solution for
128//! this particular case as written; frankly I believe that almost the only time you should
129//! downcast an Any trait object (or a mopafied trait object) is with a generic parameter, when
130//! producing something like `AnyMap`, for example. If you control *all* the code, `Any` trait
131//! objects are probably not the right solution; they’re good for cases with user-defined
132//! types across a variety of libraries. But the question of purpose and suitability is open, and I
133//! don’t have a really good example of such a use case here at present. TODO.
134
135#![cfg_attr(feature = "no_std", no_std)]
136
137#[cfg(all(test, feature = "no_std"))]
138extern crate std;
139
140#[cfg(not(feature = "no_std"))]
141extern crate std as core;
142
143use core::any::Any as StdAny;
144use core::any::TypeId;
145
146/// A type to emulate dynamic typing.
147///
148/// This is a simple wrapper around `std::any::Any` which exists for technical reasons.
149/// Every type that implements `std::any::Any` implements this `Any`.
150///
151/// See the [`std::any::Any` documentation](http://doc.rust-lang.org/std/any/trait.Any.html) for
152/// more details.
153///
154/// Any traits to be mopafied must extend this trait.
155pub trait Any: StdAny {
156 /// Gets the `TypeId` of `self`.
157 #[doc(hidden)]
158 fn get_type_id(&self) -> TypeId;
159}
160
161impl<T: StdAny> Any for T {
162 fn get_type_id(&self) -> TypeId { TypeId::of::<T>() }
163}
164
165// Not using core::any::TraitObject, even if feature = "unstable", because of its feature(core)
166// dependency. It’d be possible to arrange, but it’d require the macro user to add feature(core).
167#[repr(C)]
168#[derive(Copy, Clone)]
169#[doc(hidden)]
170pub struct TraitObject {
171 pub data: *mut (),
172 pub vtable: *mut (),
173}
174
175/// The macro for implementing all the `Any` methods on your own trait.
176///
177/// # Instructions for use
178///
179/// 1. Make sure your trait extends `mopa::Any` (e.g. `trait Trait: mopa::Any { }`)
180///
181/// 2. Mopafy your trait (see the next subsection for specifics).
182///
183/// 3. …
184///
185/// 4. Profit!
186///
187/// ## Mopafication techniques
188///
189/// There are three ways of mopafying traits, depending on what libraries you are using.
190///
191/// 1. If you are a **normal person**:
192///
193/// ```rust
194/// # #[macro_use] extern crate mopa;
195/// trait Trait: mopa::Any { }
196/// mopafy!(Trait);
197/// # fn main() { }
198/// ```
199///
200/// 2. If you are using **libcore** but not libstd (`#![no_std]`) or liballoc, enable the `no_std`
201/// Cargo feature and write this:
202///
203/// ```rust,ignore
204/// # #![feature(core)]
205/// # #[macro_use] extern crate mopa;
206/// # extern crate core;
207/// # trait Trait: mopa::Any { }
208/// mopafy!(Trait, core = core);
209/// # fn main() { }
210/// ```
211///
212/// (This is akin to `mopafy!(Trait, core = std)` if you were using libstd.)
213///
214/// Unlike the other two techniques, this only gets you the `&Any` and `&mut Any` methods; the
215/// `Box<Any>` methods require liballoc.
216///
217/// 3. If you are using **libcore and liballoc** but not libstd (`#![no_std]`), enable the `no_std`
218/// Cargo feature and write this:
219///
220/// ```rust,ignore
221/// # #![feature(core, alloc)]
222/// # #[macro_use] extern crate mopa;
223/// # extern crate core;
224/// # extern crate alloc;
225/// # trait Trait: mopa::Any { }
226/// mopafy!(Trait, core = core, alloc = alloc);
227/// # fn main() { }
228/// ```
229///
230/// (This is akin to `mopafy!(Trait, core = std, alloc = std)` if you were using libstd; in
231/// fact, the first form is just sugar for this very thing.)
232///
233/// This gets you all the methods.
234#[macro_export]
235macro_rules! mopafy {
236 // Using libstd like a normal person? Here’s what you want, just a simple `mopafy!(Trait)`.
237 ($trait_:ident) => {
238 mopafy!($trait_, core = std, alloc = std);
239 };
240
241 // Not using libstd or liballoc? You can get the &Any and &mut Any methods by specifying what
242 // libcore is here, e.g. `mopafy!(Trait, core = core)`, but you won’t get the `Box<Any>`
243 // methods.
244 ($trait_:ident, core = $core:ident) => {
245 #[allow(dead_code)]
246 impl $trait_ {
247 /// Returns true if the boxed type is the same as `T`
248 #[inline]
249 pub fn is<T: $trait_>(&self) -> bool {
250 ::$core::any::TypeId::of::<T>() == $crate::Any::get_type_id(self)
251 }
252
253 /// Returns some reference to the boxed value if it is of type `T`, or
254 /// `None` if it isn't.
255 #[inline]
256 pub fn downcast_ref<T: $trait_>(&self) -> ::$core::option::Option<&T> {
257 if self.is::<T>() {
258 unsafe {
259 ::$core::option::Option::Some(self.downcast_ref_unchecked())
260 }
261 } else {
262 ::$core::option::Option::None
263 }
264 }
265
266 /// Returns a reference to the boxed value, blindly assuming it to be of type `T`.
267 /// If you are not *absolutely certain* of `T`, you *must not* call this.
268 #[inline]
269 pub unsafe fn downcast_ref_unchecked<T: $trait_>
270 (&self) -> &T {
271 let trait_object: $crate::TraitObject = ::$core::mem::transmute(self);
272 ::$core::mem::transmute(trait_object.data)
273 }
274
275 /// Returns some mutable reference to the boxed value if it is of type `T`, or
276 /// `None` if it isn't.
277 #[inline]
278 pub fn downcast_mut<T: $trait_>(&mut self) -> ::$core::option::Option<&mut T> {
279 if self.is::<T>() {
280 unsafe {
281 ::$core::option::Option::Some(self.downcast_mut_unchecked())
282 }
283 } else {
284 ::$core::option::Option::None
285 }
286 }
287
288 /// Returns a mutable reference to the boxed value, blindly assuming it to be of type `T`.
289 /// If you are not *absolutely certain* of `T`, you *must not* call this.
290 #[inline]
291 pub unsafe fn downcast_mut_unchecked<T: $trait_>
292 (&mut self) -> &mut T {
293 let trait_object: $crate::TraitObject = ::$core::mem::transmute(self);
294 ::$core::mem::transmute(trait_object.data)
295 }
296 }
297 };
298
299 // Not using libstd? You can get the Box<Any> methods by specifying what liballoc is here,
300 // e.g. `mopafy!(Trait, alloc = alloc)`
301 ($trait_:ident, core = $core:ident, alloc = $alloc:ident) => {
302 mopafy!($trait_, core = $core);
303
304 #[allow(dead_code)]
305 impl $trait_ {
306 /// Returns the boxed value if it is of type `T`, or `Err(Self)` if it isn't.
307 #[inline]
308 pub fn downcast<T: $trait_>(self: ::$alloc::boxed::Box<Self>)
309 -> ::$core::result::Result<::$alloc::boxed::Box<T>,
310 ::$alloc::boxed::Box<Self>> {
311 if self.is::<T>() {
312 unsafe {
313 ::$core::result::Result::Ok(self.downcast_unchecked())
314 }
315 } else {
316 ::$core::result::Result::Err(self)
317 }
318 }
319
320 /// Returns the boxed value, blindly assuming it to be of type `T`.
321 /// If you are not *absolutely certain* of `T`, you *must not* call this.
322 #[inline]
323 pub unsafe fn downcast_unchecked<T: $trait_>(self: ::$alloc::boxed::Box<Self>)
324 -> ::$alloc::boxed::Box<T> {
325 let trait_object: $crate::TraitObject = ::$core::mem::transmute(self);
326 ::$core::mem::transmute(trait_object.data)
327 }
328 }
329 };
330}
331
332#[cfg(test)]
333mod tests {
334 use std::prelude::v1::*;
335
336 trait Person: super::Any {
337 fn weight(&self) -> i16;
338 }
339
340 mopafy!(Person);
341
342 #[derive(Clone, Debug, PartialEq)]
343 struct Benny {
344 // (Benny is not a superhero. He can’t carry more than 256kg of food at once.)
345 kilograms_of_food: u8,
346 }
347
348 impl Person for Benny {
349 fn weight(&self) -> i16 {
350 self.kilograms_of_food as i16 + 60
351 }
352 }
353
354 #[derive(Clone, Debug, PartialEq)]
355 struct Chris;
356
357 impl Person for Chris {
358 fn weight(&self) -> i16 { -5 /* antigravity device! cool! */ }
359 }
360
361 #[test]
362 fn test_ref() {
363 let benny = Benny { kilograms_of_food: 13 };
364 let benny_ptr: *const Benny = &benny;
365 let person: &Person = &benny;
366
367 assert!(person.is::<Benny>());
368 assert_eq!(person.downcast_ref::<Benny>().map(|x| x as *const Benny), Some(benny_ptr));
369 assert_eq!(unsafe { person.downcast_ref_unchecked::<Benny>() as *const Benny }, benny_ptr);
370
371 assert!(!person.is::<Chris>());
372 assert_eq!(person.downcast_ref::<Chris>(), None);
373 }
374
375 #[test]
376 fn test_mut() {
377 let mut benny = Benny { kilograms_of_food: 13 };
378 let benny_ptr: *const Benny = &benny;
379 let person: &mut Person = &mut benny;
380 assert!(person.is::<Benny>());
381 assert_eq!(person.downcast_ref::<Benny>().map(|x| x as *const Benny), Some(benny_ptr));
382 assert_eq!(person.downcast_mut::<Benny>().map(|x| &*x as *const Benny), Some(benny_ptr));
383 assert_eq!(unsafe { person.downcast_ref_unchecked::<Benny>() as *const Benny }, benny_ptr);
384 assert_eq!(unsafe { &*person.downcast_mut_unchecked::<Benny>() as *const Benny }, benny_ptr);
385
386 assert!(!person.is::<Chris>());
387 assert_eq!(person.downcast_ref::<Chris>(), None);
388 assert_eq!(person.downcast_mut::<Chris>(), None);
389 }
390
391 #[test]
392 fn test_box() {
393 let mut benny = Benny { kilograms_of_food: 13 };
394 let mut person: Box<Person> = Box::new(benny.clone());
395 assert!(person.is::<Benny>());
396 assert_eq!(person.downcast_ref::<Benny>(), Some(&benny));
397 assert_eq!(person.downcast_mut::<Benny>(), Some(&mut benny));
398 assert_eq!(person.downcast::<Benny>().map(|x| *x).ok(), Some(benny.clone()));
399
400 person = Box::new(benny.clone());
401 assert_eq!(unsafe { person.downcast_ref_unchecked::<Benny>() }, &benny);
402 assert_eq!(unsafe { person.downcast_mut_unchecked::<Benny>() }, &mut benny);
403 assert_eq!(unsafe { *person.downcast_unchecked::<Benny>() }, benny);
404
405 person = Box::new(benny.clone());
406 assert!(!person.is::<Chris>());
407 assert_eq!(person.downcast_ref::<Chris>(), None);
408 assert_eq!(person.downcast_mut::<Chris>(), None);
409 assert!(person.downcast::<Chris>().err().is_some());
410 }
411}