any_trait/
lib.rs

1#![feature(const_trait_impl)]
2#![feature(const_cmp)]
3#![no_std]
4
5//! # AnyTrait
6//!
7//! This is a **no_std** crate that lets you cast from:
8//! * your concrete type
9//! * `&dyn AnyTrait`
10//! to:
11//! * the concrete type
12//! * any other trait implemented by your type
13//! * `&dyn AnyTrait`
14//!
15//! If the trait implements `AnyTrait`, that too can be cast to:
16//! * the concrete type
17//! * any other trait implemented by your type
18//! * `&dyn AnyTrait`
19//!
20//! *This is not zero-cost, since at any cast we need to go through the
21//! list of all possible subtraits.*
22//!
23//! This will (almost) enable you to do OOP in rust, but if this is your goal
24//! we still ask you to kindly reconsider
25//!
26//! example usage:
27//! ```rust
28//! use any_trait::{AnySubTrait, AnyTrait, AsAnyTrait, AnyTraitCast};
29//! trait TA {}
30//! trait TB : AnyTrait {} // if a trait implements `AnyTrait` you can upi/downcast
31//! #[derive(AnySubTrait)]
32//! #[any_sub_trait(TA, TB)] // must include all traits you want to downcast to
33//! struct Concrete {
34//!     // whatever
35//! }
36//! impl TA for Concrete {}
37//! impl TB for Concrete {}
38//! fn test() {
39//!     let c = Concrete{};
40//!
41//!     let a = c.as_anytrait();
42//!
43//!     let ta :&dyn TA = a.cast_ref::<dyn TA>().unwrap();
44//!     let tb :&dyn TB = a.cast_ref::<dyn TB>().unwrap();
45//!
46//!     let ta_from_tb : &dyn TA = tb.cast_ref::<dyn TA>().unwrap();
47//!
48//!     let a2 = tb.as_anytrait();
49//!     let c_ref : &Concrete = a2.cast_ref::<Concrete>().unwrap();
50//! }
51//! ```
52pub mod anyptr;
53pub mod typeidconst;
54
55use anyptr::AnyPtr;
56use typeidconst::TypeIdConst;
57
58pub use ::any_trait_macro::AnySubTrait;
59
60/// # AnyTrait
61///
62/// **Don't implement manually**
63///
64/// use `#[derive(AnySubTrait)]`
65///
66/// <br/>
67///
68/// Imagine a Concrete type and all its subtraits\
69/// AnyTrait lets you walk up and down the traits safely
70///
71/// With `::core::any::Any` you can only cast between the concrete type
72/// and `Any`.\
73/// With `AnyTrait` you can do that, plus any other trait in the middle
74///
75/// *`AnyTrait` is not necessarily fast as it needs check and track
76/// the list of traits you are allowed to cast to.*
77pub trait AnyTrait: 'static {
78    /// returns a list of all possible traits that you can up/downcast to\
79    /// This list always has at least two elements:
80    /// * id 0: `TypeIdConst::of::<dyn AnyType>`
81    /// * id 1: `TypeIdConst::of::<YourConcreteType>`
82    ///
83    /// The reset of the list is currently unordered, will change as soon
84    /// as we find a way to have a `const Ord` on `TypeId`
85    fn type_ids(&self) -> &'static [TypeIdConst];
86
87    /// **don't use. internal only.**
88    ///
89    /// cast `self` to a trait in the `.type_ids()` list.\
90    /// the pointer to the ref to the type in the list
91    /// is then type-erase to `AnyPtr`.
92    ///
93    /// # Safety
94    /// This is safe since you can't do anything to a `AnyPtr` by itself.
95    /// `AnyPtr::to_ptr` however is just wrong if you don't have the right
96    /// type. Again, don't use: internal only
97    ///
98    /// # Panics
99    /// If list `trait_num` exceeds `type_ids()` length
100    fn type_erase(&self, trait_num: usize) -> AnyPtr;
101    /// **don't use. internal only.**
102    ///
103    /// cast `self` to a trait in the `.type_ids()` list.\
104    /// the pointer to the ref to the type in the list
105    /// is then type-erase to `AnyPtr`.
106    ///
107    /// # Safety
108    /// This is safe since you can't do anything to a `AnyPtr` by itself
109    /// `AnyPtr::to_ptr` however is just wrong if you don't have the right
110    /// type. Again, don't use: internal only
111    ///
112    /// # Panics
113    /// If list `trait_num` exceeds `type_ids()` length
114    fn type_erase_mut(&mut self, trait_num: usize) -> AnyPtr;
115}
116
117/// upcast from the concrete type
118///
119/// (or from any other trait that implements `AnyTrait`) to `&dyn AnyTrait`
120///
121/// **Automatically implemented on all types that implement `AnyTrait`**
122pub trait AsAnyTrait: AnyTrait {
123    fn as_anytrait(&self) -> &dyn AnyTrait;
124    fn as_anytrait_mut(&mut self) -> &mut dyn AnyTrait;
125}
126
127/// (Up/Down)cast to another type
128///
129/// **Automatically implemented on all types that implement `AnyTrait`**
130///
131/// Note that this trait is **not dyn-compatible** and for that reason it is
132/// kept separate
133pub trait AnyTraitCast: AnyTrait {
134    /// Find the type in the supported trait list
135    fn trait_idx<T: ?Sized + 'static>(&self) -> Option<usize>;
136    /// (Up/Down)cast to a ref if the type is supported.
137    ///
138    /// Both Upcast and Downcast work, as long as the type is supported
139    fn cast_ref<D: ?Sized + 'static>(&self) -> Option<&D>;
140    /// (Up/Down)cast to a mut ref if the type is supported.
141    ///
142    /// Both Upcast and Downcast work, as long as the type is supported
143    fn cast_mut<D: ?Sized + 'static>(&mut self) -> Option<&mut D>;
144}
145
146// everybody can have the same implementation as the `dyn Any` is always
147// the first type in the list
148impl<T: AnyTrait + ?Sized> AsAnyTrait for T {
149    /// upcast to `&dyn AnyTrait`
150    #[inline]
151    fn as_anytrait(&self) -> &dyn AnyTrait {
152        let erased = self.type_erase(0);
153        #[allow(unsafe_code)]
154        unsafe {
155            let any = erased.to_ptr::<dyn AnyTrait>();
156
157            return any.as_ref();
158        }
159    }
160    /// upcast to `&mut dyn AnyTrait`
161    fn as_anytrait_mut(&mut self) -> &mut dyn AnyTrait {
162        let erased = self.type_erase(0);
163        #[allow(unsafe_code)]
164        unsafe {
165            let mut any = erased.to_ptr::<dyn AnyTrait>();
166
167            return any.as_mut();
168        }
169    }
170}
171
172impl<T: AnyTrait + ?Sized> AnyTraitCast for T {
173    /// Search the list of possible traits.
174    ///
175    /// If `self` can be cast to the generic parameter,
176    /// return the index of the type in the list
177    #[inline]
178    fn trait_idx<D: ?Sized + 'static>(&self) -> Option<usize> {
179        let t = TypeIdConst::of::<D>();
180
181        let all_traits = self.type_ids();
182
183        for it in all_traits.iter().enumerate() {
184            if *it.1 == t {
185                return Some(it.0);
186            }
187        }
188
189        return None;
190
191        /* Waiting for const Ord on TypeId...
192        if all_traits[0] == t {
193            return Some(0);
194        }
195        if all_traits[1] == t {
196            return Some(1);
197        }
198        let sub_traits = &all_traits[2..];
199
200        // 128: carefully chosen completely at random
201        if sub_traits.len() < 128 {
202            match sub_traits.iter().enumerate().find(|x| *x.1 == t) {
203                Some((idx, _)) => Some(2 + idx),
204                None => None,
205            }
206        } else {
207            match sub_traits.binary_search(&t) {
208                Ok(idx) => Some(2 + idx),
209                Err(_) => None,
210            }
211        }
212        */
213    }
214
215    /// Safe cast to reference to a generic type.
216    ///
217    /// Only return Some(...) if it is safe to do so.
218    #[inline]
219    fn cast_ref<D: ?Sized + 'static>(&self) -> Option<&D> {
220        let Some(trait_idx) = self.trait_idx::<D>() else {
221            return None;
222        };
223
224        let erased = self.type_erase(trait_idx);
225        #[allow(unsafe_code)]
226        unsafe {
227            let any = erased.to_ptr::<D>();
228
229            return Some(any.as_ref());
230        }
231    }
232
233    /// Safe cast to mutable reference to a generic type.
234    ///
235    /// Only return Some(...) if it is safe to do so.
236    #[inline]
237    fn cast_mut<D: ?Sized + 'static>(&mut self) -> Option<&mut D> {
238        let Some(trait_idx) = self.trait_idx::<D>() else {
239            return None;
240        };
241
242        let erased = self.type_erase_mut(trait_idx);
243        #[allow(unsafe_code)]
244        unsafe {
245            let mut any = erased.to_ptr::<D>();
246
247            return Some(any.as_mut());
248        }
249    }
250}