downcast/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2#![cfg_attr(feature = "nightly", feature(core_intrinsics, try_from))]
3
4#[cfg(not(feature = "std"))]
5mod std {
6    pub use core::*;
7}
8
9use std::any::Any as StdAny;
10use std::any::TypeId;
11#[cfg(feature = "nightly")]
12use std::convert::TryFrom;
13#[cfg(feature = "std")]
14use std::error::Error;
15#[cfg(feature = "nightly")]
16use std::intrinsics;
17use std::fmt::{self, Debug, Display};
18use std::mem;
19
20// ++++++++++++++++++++ Any ++++++++++++++++++++
21
22#[cfg(feature = "nightly")]
23fn type_name<T: StdAny + ?Sized>() -> &'static str { unsafe { intrinsics::type_name::<T>() } }
24#[cfg(not(feature = "nightly"))]
25fn type_name<T: StdAny + ?Sized>() -> &'static str { "[ONLY ON NIGHTLY]" }
26
27pub trait Any: StdAny {
28    /// TODO: once 1.33.0 is the minimum supported compiler version, remove
29    /// Any::type_id_compat and use StdAny::type_id instead.
30    /// https://github.com/rust-lang/rust/issues/27745
31    fn type_id_compat(&self) -> TypeId { TypeId::of::<Self>() }
32    #[doc(hidden)]
33    fn type_name(&self) -> &'static str { type_name::<Self>() }
34}
35
36impl<T> Any for T where T: StdAny + ?Sized {}
37
38// ++++++++++++++++++++ TypeMismatch ++++++++++++++++++++
39
40#[derive(Debug, Clone, Copy)]
41pub struct TypeMismatch {
42    expected: &'static str,
43    found: &'static str,
44}
45
46impl TypeMismatch {
47    pub fn new<T, O>(found_obj: &O) -> Self
48        where T: Any + ?Sized, O: Any + ?Sized
49    {
50        TypeMismatch {
51            expected: type_name::<T>(),
52            found: found_obj.type_name(),
53        }
54    }
55}
56
57impl Display for TypeMismatch {
58    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
59        write!(fmt, "Type mismatch: Expected '{}', found '{}'!", self.expected, self.found)
60    }
61}
62
63#[cfg(feature = "std")]
64impl Error for TypeMismatch {
65    fn description(&self) -> &str { "Type mismatch" }
66}
67
68// ++++++++++++++++++++ DowncastError ++++++++++++++++++++
69
70pub struct DowncastError<O> {
71    mismatch: TypeMismatch,
72    object: O,
73}
74
75impl<O> DowncastError<O> {
76    pub fn new(mismatch: TypeMismatch, object: O) -> Self {
77        Self {
78            mismatch: mismatch,
79            object: object,
80        }
81    }
82    pub fn type_mismatch(&self) -> TypeMismatch { self.mismatch }
83    pub fn into_object(self) -> O { self.object }
84}
85
86impl<O> Debug for DowncastError<O> {
87    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
88        fmt.debug_struct("DowncastError")
89            .field("mismatch", &self.mismatch)
90            .finish()
91    }
92}
93
94impl<O> Display for DowncastError<O> {
95    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
96        Display::fmt(&self.mismatch, fmt)
97    }
98}
99
100#[cfg(feature = "std")]
101impl<O> Error for DowncastError<O> {
102    fn description(&self) -> &str { self.mismatch.description() }
103}
104
105// ++++++++++++++++++++ Downcast ++++++++++++++++++++
106
107#[derive(Clone, Copy)]
108struct TraitObject {
109    pub data: *mut (),
110    pub vtable: *mut (),
111}
112
113#[inline]
114fn to_trait_object<T: ?Sized>(obj: &T) -> TraitObject {
115    assert_eq!(mem::size_of::<&T>(), mem::size_of::<TraitObject>());
116    unsafe { *((&obj) as *const &T as *const TraitObject) }
117}
118
119pub trait Downcast<T>: Any
120    where T: Any
121{
122    fn is_type(&self) -> bool { self.type_id_compat() == TypeId::of::<T>() }
123
124    unsafe fn downcast_ref_unchecked(&self) -> &T { &*(to_trait_object(self).data as *mut T) }
125
126    fn downcast_ref(&self) -> Result<&T, TypeMismatch> {
127        if self.is_type() {
128            Ok(unsafe { self.downcast_ref_unchecked() })
129        } else {
130            Err(TypeMismatch::new::<T, Self>(self))
131        }
132    }
133
134    unsafe fn downcast_mut_unchecked(&mut self) -> &mut T {
135        &mut *(to_trait_object(self).data as *mut T)
136    }
137
138    fn downcast_mut(&mut self) -> Result<&mut T, TypeMismatch> {
139        if self.is_type() {
140            Ok(unsafe { self.downcast_mut_unchecked() })
141        } else {
142            Err(TypeMismatch::new::<T, Self>(self))
143        }
144    }
145
146    #[cfg(feature = "std")]
147    unsafe fn downcast_unchecked(self: Box<Self>) -> Box<T> {
148        let ret: Box<T> = Box::from_raw(to_trait_object(&*self).data as *mut T);
149        mem::forget(self);
150        ret
151    }
152
153    #[cfg(feature = "std")]
154    fn downcast(self: Box<Self>) -> Result<Box<T>, DowncastError<Box<Self>>> {
155        if self.is_type() {
156            Ok(unsafe { self.downcast_unchecked() })
157        } else {
158            let mismatch = TypeMismatch::new::<T, Self>(&*self);
159            Err(DowncastError::new(mismatch, self))
160        }
161    }
162}
163
164// ++++++++++++++++++++ macros ++++++++++++++++++++
165
166#[doc(hidden)]
167pub mod _std {
168    pub use std::*;
169}
170
171/// Implements [`Downcast`](trait.Downcast.html) for your trait-object-type.
172///
173/// ```ignore
174/// impl_downcast!(Foo);
175/// impl_downcast!(<B> Foo<B> where B: Bar);
176/// impl_downcast!(<B> Foo<Bar = B>);
177/// ```
178///
179/// expands to
180///
181/// ```ignore
182/// impl<T> Downcast<T> for Foo
183///     where T: Any
184/// {}
185///
186/// impl<T, B> Downcast<T> for Foo<B>
187///     where T: Any, B: Bar
188/// {}
189///
190/// impl<T, B> Downcast<T> for Foo<Bar = B>
191///     where T: Any
192/// {}
193/// ```
194#[macro_export]
195macro_rules! impl_downcast {
196    (<$($params:ident),+ $(,)*> $base:ty $(where $($bounds:tt)+)*) => {
197        impl<_T: $crate::Any, $($params),+> $crate::Downcast<_T> for $base
198            where _T: $crate::Any, $($params: 'static,)* $($($bounds)+)*
199        {}
200    };
201    ($base:ty) => {
202        impl<_T: $crate::Any> $crate::Downcast<_T> for $base
203            where _T: $crate::Any
204        {}
205    };
206}
207
208#[doc(hidden)]
209#[macro_export]
210macro_rules! downcast_methods_core {
211    (@items) => {
212        #[allow(unused)]
213        pub fn is<_T>(&self) -> bool
214            where _T: $crate::Any, Self: $crate::Downcast<_T>
215        {
216            $crate::Downcast::<_T>::is_type(self)
217        }
218
219        #[allow(unused)]
220        pub unsafe fn downcast_ref_unchecked<_T>(&self) -> &_T
221            where _T: $crate::Any, Self: $crate::Downcast<_T>
222        {
223            $crate::Downcast::<_T>::downcast_ref_unchecked(self)
224        }
225
226        #[allow(unused)]
227        pub fn downcast_ref<_T>(&self) -> $crate::_std::result::Result<&_T, $crate::TypeMismatch>
228            where _T: $crate::Any, Self: $crate::Downcast<_T>
229        {
230            $crate::Downcast::<_T>::downcast_ref(self)
231        }
232
233        #[allow(unused)]
234        pub unsafe fn downcast_mut_unchecked<_T>(&mut self) -> &mut _T
235            where _T: $crate::Any, Self: $crate::Downcast<_T>
236        {
237            $crate::Downcast::<_T>::downcast_mut_unchecked(self)
238        }
239
240        #[allow(unused)]
241        pub fn downcast_mut<_T>(&mut self) -> $crate::_std::result::Result<&mut _T, $crate::TypeMismatch>
242            where _T: $crate::Any, Self: $crate::Downcast<_T>
243        {
244            $crate::Downcast::<_T>::downcast_mut(self)
245        }
246    };
247    (<$($params:ident),+ $(,)*> $base:ty $(where $($bounds:tt)+)*) => {
248        impl<$($params),+> $base
249            where $($params: 'static,)* $($($bounds)+)*
250        {
251            downcast_methods_core!(@items);
252        }
253    };
254    ($base:ty) => {
255        impl $base {
256            downcast_methods_core!(@items);
257        }
258    };
259}
260
261#[doc(hidden)]
262#[macro_export]
263macro_rules! downcast_methods_std {
264    (@items) => {
265        downcast_methods_core!(@items);
266
267        #[allow(unused)]
268        pub unsafe fn downcast_unchecked<_T>(self: $crate::_std::boxed::Box<Self>) -> $crate::_std::boxed::Box<_T>
269            where _T: $crate::Any, Self: $crate::Downcast<_T>
270        {
271            $crate::Downcast::<_T>::downcast_unchecked(self)
272        }
273
274        #[allow(unused)]
275        pub fn downcast<_T>(self: $crate::_std::boxed::Box<Self>) -> $crate::_std::result::Result<$crate::_std::boxed::Box<_T>, $crate::DowncastError<Box<Self>>>
276            where _T: $crate::Any, Self: $crate::Downcast<_T>
277        {
278            $crate::Downcast::<_T>::downcast(self)
279        }
280    };
281    (<$($params:ident),+ $(,)*> $base:ty $(where $($bounds:tt)+)*) => {
282        impl<$($params),+> $base
283            $(where $($bounds)+)*
284        {
285            downcast_methods_std!(@items);
286        }
287    };
288    ($base:ty) => {
289        impl $base {
290            downcast_methods_std!(@items);
291        }
292    };
293}
294
295/// Generate `downcast`-methods for your trait-object-type.
296///
297/// ```ignore
298/// downcast_methods!(Foo);
299/// downcast_methods!(<B> Foo<B> where B: Bar);
300/// downcast_methods!(<B> Foo<Bar = B>);
301/// ```
302///
303/// ```ignore
304/// /* 1st */ impl Foo {
305/// /* 2nd */ impl<B> Foo<B> where B: Bar {
306/// /* 3nd */ impl<B> Foo<Bar = B> {
307///
308///     pub fn is<T>(&self) -> bool
309///         where T: Any, Self: Downcast<T>
310///     { ... }
311///
312///     pub unsafe fn downcast_ref_unchecked<T>(&self) -> &T
313///         where T: Any, Self: Downcast<T>
314///     { ... }
315///
316///     pub fn downcast_ref<T>(&self) -> Result<&T, TypeMismatch>
317///         where T: Any, Self: Downcast<T>
318///     { ... }
319///
320///     pub unsafe fn downcast_mut_unchecked<T>(&mut self) -> &mut T
321///         where T: Any, Self: Downcast<T>
322///     { ... }
323///
324///     pub fn downcast_mut<T>(&mut self) -> Result<&mut T, TypeMismatch>
325///         where T: Any, Self: Downcast<T>
326///     { ... }
327///
328///     pub unsafe fn downcast_unchecked<T>(self: Box<Self>) -> Box<T>
329///         where T: Any, Self: Downcast<T>
330///     { ... }
331/// }
332/// ```
333#[cfg(not(feature = "std"))]
334#[macro_export]
335macro_rules! downcast_methods {
336    ($($tt:tt)+) => { downcast_methods_core!($($tt)+); }
337}
338
339/// Generate `downcast`-methods for your trait-object-type.
340///
341/// ```ignore
342/// downcast_methods!(Foo);
343/// downcast_methods!(<B> Foo<B> where B: Bar);
344/// downcast_methods!(<B> Foo<Bar = B>);
345/// ```
346///
347/// ```ignore
348/// /* 1st */ impl Foo {
349/// /* 2nd */ impl<B> Foo<B> where B: Bar {
350/// /* 3nd */ impl<B> Foo<Bar = B> {
351///
352///     pub fn is<T>(&self) -> bool
353///         where T: Any, Self: Downcast<T>
354///     { ... }
355///
356///     pub unsafe fn downcast_ref_unchecked<T>(&self) -> &T
357///         where T: Any, Self: Downcast<T>
358///     { ... }
359///
360///     pub fn downcast_ref<T>(&self) -> Result<&T, TypeMismatch>
361///         where T: Any, Self: Downcast<T>
362///     { ... }
363///
364///     pub unsafe fn downcast_mut_unchecked<T>(&mut self) -> &mut T
365///         where T: Any, Self: Downcast<T>
366///     { ... }
367///
368///     pub fn downcast_mut<T>(&mut self) -> Result<&mut T, TypeMismatch>
369///         where T: Any, Self: Downcast<T>
370///     { ... }
371///
372///     pub unsafe fn downcast_unchecked<T>(self: Box<Self>) -> Box<T>
373///         where T: Any, Self: Downcast<T>
374///     { ... }
375///
376/// pub fn downcast<T>(self: Box<Self>) -> Result<Box<T>,
377/// DowncastError<Box<T>>>
378///         where T: Any, Self: Downcast<T>
379///     { ... }
380/// }
381/// ```
382#[cfg(feature = "std")]
383#[macro_export]
384macro_rules! downcast_methods {
385    ($($tt:tt)+) => { downcast_methods_std!($($tt)+); }
386}
387
388/// Implements [`Downcast`](trait.downcast.html) and generates
389/// `downcast`-methods for your trait-object-type.
390///
391/// See [`impl_downcast`](macro.impl_downcast.html),
392/// [`downcast_methods`](macro.downcast_methods.html).
393#[macro_export]
394macro_rules! downcast {
395    ($($tt:tt)+) => {
396        impl_downcast!($($tt)+);
397        downcast_methods!($($tt)+);
398    }
399}
400
401// NOTE: We only implement the trait, because implementing the methods won't
402// be possible when we replace downcast::Any by std::any::Any.
403mod any_impls {
404    use super::Any;
405
406    impl_downcast!(Any);
407    impl_downcast!((Any + Send));
408    impl_downcast!((Any + Sync));
409    impl_downcast!((Any + Send + Sync));
410}