dynamic_cast/
lib.rs

1#![feature(allocator_api)]
2#![feature(ptr_metadata)]
3
4#![no_std]
5
6#[doc=include_str!("../README.md")]
7type _DocTestReadme = ();
8
9extern crate alloc;
10
11use alloc::boxed::Box;
12use alloc::rc::Rc;
13use alloc::sync::Arc;
14use arraybox::ArrayBox;
15use downcast_rs::{Downcast, impl_downcast};
16use core::alloc::Allocator;
17use core::any::TypeId;
18use core::ops::Deref;
19use core::ptr::{self, DynMetadata, Pointee};
20
21#[doc(hidden)]
22pub use core::any::TypeId as core_any_TypeId;
23#[doc(hidden)]
24pub use core::option::Option as core_option_Option;
25
26/// Stack-allocated [`dyn IsInterfaceMetadata`](IsInterfaceMetadata).
27pub type BoxedInterfaceMetadata =
28    ArrayBox<'static, dyn IsInterfaceMetadata, InterfaceMetadata<dyn IsInterfaceMetadata>>
29;
30
31/// [`InterfaceMetadata`] with erased generic argument.
32///
33/// Supports downcasting back to specific `InterfaceMetadata`.
34pub trait IsInterfaceMetadata: Downcast { }
35
36impl_downcast!(IsInterfaceMetadata);
37
38/// A newtype wrap for [`DynMetadata`].
39///
40/// Designed for type erasure by coercing to [`dyn IsIntefaceMetadata`](IsInterfaceMetadata).
41pub struct InterfaceMetadata<DynInterface: ?Sized>(pub DynMetadata<DynInterface>);
42
43impl<DynInterface: ?Sized + 'static> IsInterfaceMetadata for InterfaceMetadata<DynInterface> { }
44
45/// Provides runtime information about implemented traits.
46///
47/// # Safety
48///
49/// An implementer does not allowed to return from
50/// [`get_interface_metadata`](SupportsInterfaces::get_interface_metadata)
51/// something except `None` in all cases, excluding the case
52/// when `dyn_interface_id` is a type id of `dyn SomeTrait`, and the implementer implements
53/// `SomeTrait`. If `get_interface_metadata` returns `Some(SomeTrait metadata)`, it should return appropriate
54/// correct metadata for `self as dyn SomeTrait` thick pointers.
55pub unsafe trait SupportsInterfaces {
56    fn get_interface_metadata(&self, dyn_interface_id: TypeId) -> Option<BoxedInterfaceMetadata>;
57}
58
59/// Runtime-checking safe cast for shared references.
60pub fn dyn_cast_ref<T: SupportsInterfaces + ?Sized, DynInterface: ?Sized + 'static>(
61    x: &T
62) -> Option<&DynInterface> where DynInterface: Pointee<Metadata=DynMetadata<DynInterface>> {
63    unsafe { dyn_cast_raw(x, |x| (x as *const T, ()), |x, ()| &*x) }
64}
65
66/// Runtime-checking safe cast for unique references.
67pub fn dyn_cast_mut<T: SupportsInterfaces + ?Sized, DynInterface: ?Sized + 'static>(
68    x: &mut T
69) -> Option<&mut DynInterface> where DynInterface: Pointee<Metadata=DynMetadata<DynInterface>> {
70    unsafe { dyn_cast_raw_mut(x, |x| (x as *mut T, ()), |x, ()| &mut *x) }
71}
72
73/// Runtime-checking safe cast for [`Box`]ed objects.
74pub fn dyn_cast_box<T: SupportsInterfaces + ?Sized, DynInterface: ?Sized + 'static, A: Allocator>(
75    x: Box<T, A>
76) -> Option<Box<DynInterface, A>> where DynInterface: Pointee<Metadata=DynMetadata<DynInterface>> {
77    unsafe { dyn_cast_raw_mut(x, Box::into_raw_with_allocator, Box::from_raw_in) }
78}
79
80/// Runtime-checking safe cast for [`Rc`]ed objects.
81pub fn dyn_cast_rc<T: SupportsInterfaces + ?Sized, DynInterface: ?Sized + 'static>(
82    x: Rc<T>
83) -> Option<Rc<DynInterface>> where
84    DynInterface: Pointee<Metadata=DynMetadata<DynInterface>>,
85    T: Pointee<Metadata=DynMetadata<T>>
86{
87    unsafe { dyn_cast_raw(x, |x| (Rc::into_raw(x), ()), |x, ()| Rc::from_raw(x)) }
88}
89
90/// Runtime-checking safe cast for [`Arc`]ed objects.
91pub fn dyn_cast_arc<T: SupportsInterfaces + ?Sized, DynInterface: ?Sized + 'static>(
92    x: Arc<T>
93) -> Option<Arc<DynInterface>> where
94    DynInterface: Pointee<Metadata=DynMetadata<DynInterface>>,
95    T: Pointee<Metadata=DynMetadata<T>>
96{
97    unsafe { dyn_cast_raw(x, |x| (Arc::into_raw(x), ()), |x, ()| Arc::from_raw(x)) }
98}
99
100/// Generic runtime-checking safe cast for mutable smart pointers.
101///
102/// Intended for creating specific casting functions for custom smart pointers.
103///
104/// # Safety
105///
106/// The function converts original smart pointer to a (possibly thick) mutable raw pointer using
107/// the `into_raw_parts` callback,
108/// then forms new thick mutable raw pointer replacing metadata with `DynMetadata<DynInterface>`,
109/// then calls the `from_raw_parts` unsafe callback to construct smart pointer from the last pointer.
110/// Calling the `dyn_cast_raw_mut` function is safe iff this last `from_raw_parts` call is safe.
111pub unsafe fn dyn_cast_raw_mut<
112    T: SupportsInterfaces + ?Sized,
113    DynInterface: ?Sized + 'static,
114    X: Deref<Target=T>,
115    Y,
116    A
117>(
118    x: X,
119    into_raw_parts: fn(X) -> (*mut T, A),
120    from_raw_parts: unsafe fn(*mut DynInterface, A) -> Y
121) -> Option<Y> where DynInterface: Pointee<Metadata=DynMetadata<DynInterface>> {
122    let metadata = x.get_interface_metadata(TypeId::of::<DynInterface>())?;
123    let metadata = metadata.downcast_ref::<InterfaceMetadata<DynInterface>>()
124        .unwrap_or_else(|| panic!("invalid get_dyn_cast_metadata implementation"))
125        .0
126    ;
127    let (raw_ptr, a) = into_raw_parts(x);
128    let raw_ptr = raw_ptr.to_raw_parts().0;
129    let raw_ptr = ptr::from_raw_parts_mut(raw_ptr, metadata);
130    let x = from_raw_parts(raw_ptr, a);
131    Some(x)
132}
133
134/// Generic runtime-checking safe cast for immutable smart pointers.
135///
136/// Intended for creating specific casting functions for custom smart pointers.
137///
138/// # Safety
139///
140/// The function converts original smart pointer to a (possibly thick) immutable raw pointer using
141/// the `into_raw_parts` callback,
142/// then forms new thick immutable raw pointer replacing metadata with `DynMetadata<DynInterface>`,
143/// then calls the `from_raw_parts` unsafe callback to construct smart pointer from the last pointer.
144/// Calling the `dyn_cast_raw_mut` function is safe iff this last `from_raw_parts` call is safe.
145pub unsafe fn dyn_cast_raw<
146    T: SupportsInterfaces + ?Sized,
147    DynInterface: ?Sized + 'static,
148    X: Deref<Target=T>,
149    Y,
150    A
151>(
152    x: X,
153    into_raw_parts: fn(X) -> (*const T, A),
154    from_raw_parts: unsafe fn(*const DynInterface, A) -> Y
155) -> Option<Y> where DynInterface: Pointee<Metadata=DynMetadata<DynInterface>> {
156    let metadata = x.get_interface_metadata(TypeId::of::<DynInterface>())?;
157    let metadata = metadata.downcast_ref::<InterfaceMetadata<DynInterface>>()
158        .unwrap_or_else(|| panic!("invalid get_dyn_cast_metadata implementation"))
159        .0
160    ;
161    let (raw_ptr, a) = into_raw_parts(x);
162    let raw_ptr = raw_ptr.to_raw_parts().0;
163    let raw_ptr = ptr::from_raw_parts(raw_ptr, metadata);
164    let x = from_raw_parts(raw_ptr, a);
165    Some(x)
166}
167
168/// A base piece for [`SupportsInterfaces::get_interface_metadata`] implementation.
169///
170/// Checks if `dyn_interface_id` is a type id of `DynInterface`, and if so returns appropriate
171/// thick pointer metadata, otherwise returns `None`.
172#[inline]
173pub fn try_get_interface_metadata_for<DynInterface: ?Sized + 'static>(
174    dyn_interface_id: TypeId,
175    this: &DynInterface,
176) -> Option<BoxedInterfaceMetadata> where DynInterface: Pointee<Metadata=DynMetadata<DynInterface>> {
177    if dyn_interface_id == TypeId::of::<DynInterface>() {
178        Some(ArrayBox::new(InterfaceMetadata(ptr::metadata(this as *const DynInterface))))
179    } else {
180        None
181    }
182}
183
184/// Generates correct [`SupportsInterfaces`] implementation.
185///
186/// See crate README file for example.
187#[macro_export]
188macro_rules! impl_supports_interfaces {
189    (
190        $name:ident<$($gen_param:ident $(: $gen_bound:path)?),*> $(: $($($interface:path),+ $(,)?)?)?
191    ) => {
192        unsafe impl<$($gen_param $(: $gen_bound)?),*> $crate::SupportsInterfaces for $name<$($gen_param),*> {
193            fn get_interface_metadata(
194                &self,
195                dyn_interface_id: $crate::core_any_TypeId
196            ) -> $crate::core_option_Option<$crate::BoxedInterfaceMetadata> {
197                $($($(
198                    if let $crate::core_option_Option::Some(metadata) =
199                        $crate::try_get_interface_metadata_for::<dyn $interface>(
200                            dyn_interface_id, self
201                        )
202                    {
203                        return $crate::core_option_Option::Some(metadata);
204                    }
205                )+)?)?
206                $crate::core_option_Option::None
207            }
208        }
209    };
210
211    (
212        $name:ty $(: $($($interface:path),+ $(,)?)?)?
213    ) => {
214        unsafe impl $crate::SupportsInterfaces for $name {
215            fn get_interface_metadata(
216                &self,
217                dyn_interface_id: $crate::core_any_TypeId
218            ) -> $crate::core_option_Option<$crate::BoxedInterfaceMetadata> {
219                $($($(
220                    if
221                        let $crate::core_option_Option::Some(metadata) =
222                            $crate::try_get_interface_metadata_for::<dyn $interface>(
223                                dyn_interface_id, self
224                            )
225                    {
226                        return $crate::core_option_Option::Some(metadata);
227                    }
228                )+)?)?
229                $crate::core_option_Option::None
230            }
231        }
232    };
233}