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
38//! #[macro_use]
39//! extern crate mopa;
40//! # fn main() { }
41//! ```
42//!
43//! 2. Make `Any` (`mopa::Any`, not `std::any::Any`) a supertrait of `Person`;
44//!
45//! 3. `mopafy!(Person);`.
46//!
47//! And lo, you can now write `person.is::<Benny>()` and `person.downcast_ref::<Benny>()` and so on
48//! to your heart’s content. Simple, huh?
49//!
50//! Oh, by the way, it was actually the person on the bear’s plate. There wasn’t really anything on
51//! `Person`’s plate after all.
52//!
53//! ```rust
54//! #[macro_use]
55//! extern crate mopa;
56//!
57//! struct Bear {
58//! // This might be a pretty fat bear.
59//! fatness: u16,
60//! }
61//!
62//! impl Bear {
63//! fn eat(&mut self, person: Box<Person>) {
64//! self.fatness = (self.fatness as i16 + person.weight()) as u16;
65//! }
66//! }
67//!
68//! trait Person: mopa::Any {
69//! fn panic(&self);
70//! fn yell(&self) { println!("Argh!"); }
71//! fn sleep(&self);
72//! fn weight(&self) -> i16;
73//! }
74//!
75//! mopafy!(Person);
76//!
77//! struct Benny {
78//! // (Benny is not a superhero. He can’t carry more than 256kg of food at once.)
79//! kilograms_of_food: u8,
80//! }
81//!
82//! impl Person for Benny {
83//! fn panic(&self) { self.yell() }
84//! fn sleep(&self) { /* ... */ }
85//! fn weight(&self) -> i16 {
86//! // Who’s trying to find out? I’m scared!
87//! self.yell();
88//! self.kilograms_of_food as i16 + 60
89//! }
90//! }
91//!
92//! struct Chris;
93//!
94//! impl Chris {
95//! // Normal people wouldn’t be brave enough to hit a bear but Chris might.
96//! fn hit(&self, bear: &mut Bear) {
97//! println!("Chris hits the bear! How brave! (Or maybe stupid?)");
98//! // Meh, boundary conditions, what use are they in examples?
99//! // Chris clearly hits quite hard. Poor bear.
100//! bear.fatness -= 1;
101//! }
102//! }
103//!
104//! impl Person for Chris {
105//! fn panic(&self) { /* ... */ }
106//! fn sleep(&self) { /* ... */ }
107//! fn weight(&self) -> i16 { -5 /* antigravity device! cool! */ }
108//! }
109//!
110//! fn simulate_simulation(person: Box<Person>, bear: &mut Bear) {
111//! if person.is::<Benny>() {
112//! // None of the others do, but Benny knows this particular
113//! // bear by reputation and he’s *really* going to be worried.
114//! person.yell()
115//! }
116//! // If it happens to be Chris, he’ll hit the bear.
117//! person.downcast_ref::<Chris>().map(|chris| chris.hit(bear));
118//! bear.eat(person);
119//! }
120//!
121//! fn main() {
122//! let mut bear = Bear { fatness: 10 };
123//! simulate_simulation(Box::new(Benny { kilograms_of_food: 5 }), &mut bear);
124//! simulate_simulation(Box::new(Chris), &mut bear);
125//! }
126//! ```
127//!
128//! Now *should* you do something like this? Probably not. Enums are probably a better solution for
129//! this particular case as written; frankly I believe that almost the only time you should
130//! downcast an `Any` trait object (or a mopafied trait object) is with a generic parameter, when
131//! producing something like `AnyMap`, for example. If you control *all* the code, `Any` trait
132//! objects are probably not the right solution; they’re good for cases with user-defined
133//! types across a variety of libraries. But the question of purpose and suitability is open, and I
134//! don’t have a really good example of such a use case here at present. TODO.
135
136#![no_std]
137
138#[cfg(test)]
139#[macro_use]
140extern crate std;
141
142/// Implementation details of the `mopafy!` macro.
143#[doc(hidden)]
144pub mod __ {
145 pub use core::any::TypeId;
146 // Option and Result are in the prelude, but they might have been overridden in the macro’s
147 // scope, so we do it this way to avoid issues. (Result in particular gets overridden fairly
148 // often.)
149 pub use core::option::Option;
150 pub use core::result::Result;
151}
152
153/// A type to emulate dynamic typing.
154///
155/// This is a simple wrapper around `core::any::Any` which exists for [technical reasons][#27745].
156/// Every type that implements `core::any::Any` implements this `Any`.
157///
158/// See the [`core::any::Any` documentation](http://doc.rust-lang.org/core/any/trait.Any.html) for
159/// more details.
160///
161/// Any traits to be mopafied must extend this trait (e.g. `trait Person: mopa::Any { }`).
162///
163/// If/when [#27745] is resolved, this trait may be replaced with a simple reexport of
164/// `core::any::Any`. This will be a backwards-compatible change.
165///
166/// [#27745]: https://github.com/rust-lang/rust/issues/27745
167pub trait Any: core::any::Any {
168 /// Gets the `TypeId` of `self`. UNSTABLE; do not depend on it.
169 #[doc(hidden)]
170 fn __get_type_id(&self) -> __::TypeId;
171}
172
173impl<T: core::any::Any> Any for T {
174 fn __get_type_id(&self) -> __::TypeId {
175 __::TypeId::of::<T>()
176 }
177}
178
179/// The macro for implementing all the `Any` methods on your own trait.
180///
181/// # Instructions for use
182///
183/// 1. Make sure your trait extends `mopa::Any` (e.g. `trait Trait: mopa::Any { }`)
184///
185/// 2. Mopafy your trait (see the next subsection for specifics).
186///
187/// 3. …
188///
189/// 4. Profit!
190///
191/// ## Mopafication techniques
192///
193/// There are three ways of mopafying traits, depending on what libraries you are using.
194///
195/// 1. If you are a **normal person**:
196///
197/// ```rust
198/// # #[macro_use] extern crate mopa;
199/// trait Trait: mopa::Any { }
200/// mopafy!(Trait);
201/// # fn main() { }
202/// ```
203///
204/// 2. If you are using **libcore** but not libstd (`#![no_std]`) or liballoc, write this:
205///
206/// ```rust
207/// # #[macro_use] extern crate mopa;
208/// # trait Trait: mopa::Any { }
209/// mopafy!(Trait, only core);
210/// # fn main() { }
211/// ```
212///
213/// Unlike the other two techniques, this only gets you the `&Any` and `&mut Any` methods; the
214/// `Box<Any>` methods require liballoc.
215///
216/// 3. If you are using **libcore and liballoc** but not libstd (`#![no_std]`), bring
217/// `alloc::boxed::Box` into scope and use `mopafy!` as usual:
218///
219/// ```rust,ignore
220/// # // This doctest is ignored so that it doesn't break tests on the stable/beta rustc
221/// # // channels where #[feature] isn’t allowed.
222/// # #![feature(alloc)]
223/// # #[macro_use] extern crate mopa;
224/// # extern crate alloc;
225/// # trait Trait: mopa::Any { }
226/// use alloc::boxed::Box;
227/// mopafy!(Trait);
228/// # fn main() { }
229/// ```
230#[macro_export]
231macro_rules! mopafy {
232 // Implement the full suite of `Any` methods: those of `&Any`, `&mut Any` and `Box<Any>`.
233 //
234 // If you’re not using libstd, you’ll need to `use alloc::boxed::Box;`, or forego the
235 // `Box<Any>` methods by just using `mopafy!(Trait, only core);`.
236 ($trait_:ident) => {
237 mopafy!($trait_, only core);
238
239 #[allow(dead_code)]
240 impl $trait_ {
241 /// Returns the boxed value if it is of type `T`, or `Err(Self)` if it isn't.
242 #[inline]
243 pub fn downcast<T: $trait_>(self: Box<Self>) -> $crate::__::Result<Box<T>, Box<Self>> {
244 if self.is::<T>() {
245 unsafe {
246 $crate::__::Result::Ok(self.downcast_unchecked())
247 }
248 } else {
249 $crate::__::Result::Err(self)
250 }
251 }
252
253 /// Returns the boxed value, blindly assuming it to be of type `T`.
254 /// If you are not *absolutely certain* of `T`, you *must not* call this.
255 #[inline]
256 pub unsafe fn downcast_unchecked<T: $trait_>(self: Box<Self>) -> Box<T> {
257 Box::from_raw(Box::into_raw(self) as *mut T)
258 }
259 }
260 };
261
262 // Not using libstd/liballoc? The core functionality can do without them; you will still have
263 // the `&Any` and `&mut Any` methods but will lose the `Box<Any>` methods.
264 ($trait_:ident, only core) => {
265 #[allow(dead_code)]
266 impl $trait_ {
267 /// Returns true if the boxed type is the same as `T`
268 #[inline]
269 pub fn is<T: $trait_>(&self) -> bool {
270 $crate::__::TypeId::of::<T>() == $crate::Any::__get_type_id(self)
271 }
272
273 /// Returns some reference to the boxed value if it is of type `T`, or
274 /// `None` if it isn't.
275 #[inline]
276 pub fn downcast_ref<T: $trait_>(&self) -> $crate::__::Option<&T> {
277 if self.is::<T>() {
278 unsafe {
279 $crate::__::Option::Some(self.downcast_ref_unchecked())
280 }
281 } else {
282 $crate::__::Option::None
283 }
284 }
285
286 /// Returns a reference to the boxed value, blindly assuming it to be of type `T`.
287 /// If you are not *absolutely certain* of `T`, you *must not* call this.
288 #[inline]
289 pub unsafe fn downcast_ref_unchecked<T: $trait_>(&self) -> &T {
290 &*(self as *const Self as *const T)
291 }
292
293 /// Returns some mutable reference to the boxed value if it is of type `T`, or
294 /// `None` if it isn't.
295 #[inline]
296 pub fn downcast_mut<T: $trait_>(&mut self) -> $crate::__::Option<&mut T> {
297 if self.is::<T>() {
298 unsafe {
299 $crate::__::Option::Some(self.downcast_mut_unchecked())
300 }
301 } else {
302 $crate::__::Option::None
303 }
304 }
305
306 /// Returns a mutable reference to the boxed value, blindly assuming it to be of type `T`.
307 /// If you are not *absolutely certain* of `T`, you *must not* call this.
308 #[inline]
309 pub unsafe fn downcast_mut_unchecked<T: $trait_>(&mut self) -> &mut T {
310 &mut *(self as *mut Self as *mut T)
311 }
312 }
313 };
314}
315
316#[cfg(test)]
317mod tests {
318 use std::prelude::v1::*;
319
320 trait Person: super::Any {
321 fn weight(&self) -> i16;
322 }
323
324 mopafy!(Person);
325
326 #[derive(Clone, Debug, PartialEq)]
327 struct Benny {
328 // (Benny is not a superhero. He can’t carry more than 256kg of food at once.)
329 kilograms_of_food: u8,
330 }
331
332 impl Person for Benny {
333 fn weight(&self) -> i16 {
334 self.kilograms_of_food as i16 + 60
335 }
336 }
337
338 #[derive(Clone, Debug, PartialEq)]
339 struct Chris;
340
341 impl Person for Chris {
342 fn weight(&self) -> i16 { -5 /* antigravity device! cool! */ }
343 }
344
345 #[test]
346 fn test_ref() {
347 let benny = Benny { kilograms_of_food: 13 };
348 let benny_ptr: *const Benny = &benny;
349 let person: &Person = &benny;
350
351 assert!(person.is::<Benny>());
352 assert_eq!(person.downcast_ref::<Benny>().map(|x| x as *const Benny), Some(benny_ptr));
353 assert_eq!(unsafe { person.downcast_ref_unchecked::<Benny>() as *const Benny }, benny_ptr);
354
355 assert!(!person.is::<Chris>());
356 assert_eq!(person.downcast_ref::<Chris>(), None);
357 }
358
359 #[test]
360 fn test_mut() {
361 let mut benny = Benny { kilograms_of_food: 13 };
362 let benny_ptr: *const Benny = &benny;
363 let person: &mut Person = &mut benny;
364 assert!(person.is::<Benny>());
365 assert_eq!(person.downcast_ref::<Benny>().map(|x| x as *const Benny), Some(benny_ptr));
366 assert_eq!(person.downcast_mut::<Benny>().map(|x| &*x as *const Benny), Some(benny_ptr));
367 assert_eq!(unsafe { person.downcast_ref_unchecked::<Benny>() as *const Benny }, benny_ptr);
368 assert_eq!(unsafe { &*person.downcast_mut_unchecked::<Benny>() as *const Benny }, benny_ptr);
369
370 assert!(!person.is::<Chris>());
371 assert_eq!(person.downcast_ref::<Chris>(), None);
372 assert_eq!(person.downcast_mut::<Chris>(), None);
373 }
374
375 #[test]
376 fn test_box() {
377 let mut benny = Benny { kilograms_of_food: 13 };
378 let mut person: Box<Person> = Box::new(benny.clone());
379 assert!(person.is::<Benny>());
380 assert_eq!(person.downcast_ref::<Benny>(), Some(&benny));
381 assert_eq!(person.downcast_mut::<Benny>(), Some(&mut benny));
382 assert_eq!(person.downcast::<Benny>().map(|x| *x).ok(), Some(benny.clone()));
383
384 person = Box::new(benny.clone());
385 assert_eq!(unsafe { person.downcast_ref_unchecked::<Benny>() }, &benny);
386 assert_eq!(unsafe { person.downcast_mut_unchecked::<Benny>() }, &mut benny);
387 assert_eq!(unsafe { *person.downcast_unchecked::<Benny>() }, benny);
388
389 person = Box::new(benny.clone());
390 assert!(!person.is::<Chris>());
391 assert_eq!(person.downcast_ref::<Chris>(), None);
392 assert_eq!(person.downcast_mut::<Chris>(), None);
393 assert!(person.downcast::<Chris>().err().is_some());
394 }
395}