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}