any_trait/
lib.rs

1//! # AnyTrait
2//!
3//! **AnyTrait** lets you upcast to a generic `&dyn AnyTrait` like
4//! [`::core::any::Any`]\
5//! but instead of just allowing you to downcast back to
6//! the concrete type, it also lets you downcast to any trait used by your type
7//!
8//! *This is not zero-cost, since at any cast we need to go through the
9//! list of all possible subtraits.*
10//!
11//! This will (almost) enable you to do OOP in rust, but if this is your goal
12//! we still ask you to kindly reconsider
13//!
14//! example usage:
15//! ```rust
16//! use any_trait::{AnySubTrait, AnyTrait, AsAnyTrait};
17//! trait TA {}
18//! trait TB : AnyTrait {} // if a trait implements `AnyTrait` you can upcast
19//! #[derive(AnySubTrait)]
20//! #[any_sub_trait(TA, TB)] // must include all traits you want to downcast to
21//! struct Concrete {
22//!     // whatever
23//! }
24//! impl TA for Concrete {}
25//! impl TB for Concrete {}
26//! fn test() {
27//!     let c = Concrete{};
28//!     let a = c.as_anytrait();
29//!
30//!     let ta :&dyn TA = a.downcast_ref::<dyn TA>().unwrap();
31//!     let tb :&dyn TB = a.downcast_ref::<dyn TB>().unwrap();
32//!
33//!     let a2 = tb.as_anytrait();
34//!     let c_ref : &Concrete = a2.downcast_ref::<Concrete>().unwrap();
35//! }
36//! ```
37
38#![feature(const_type_name)]
39#![no_std]
40pub mod typeidconst;
41
42use typeidconst::TypeIdConst;
43
44pub use ::any_trait_macro::AnySubTrait;
45
46/// # AnyTrait
47///
48/// **Don't implement manually**
49///
50/// use `#[derive(AnySubTrait)]`
51///
52/// <br/>
53///
54/// Imagine a Concrete type and all its subtraits\
55/// AnyTrait lets you walk up and down the traits safely
56///
57/// With `::core::any::Any` you can only cast between the concrete type
58/// and `Any`.\
59/// With `AnyTrait` you can do that, plus any other trait in the middle
60///
61/// *`AnyTrait` is not necessarily fast as it needs check and track
62/// the list of traits you are allowed to cast to.*
63pub trait AnyTrait: 'static {
64    /// returns a list of all possible traits that you can up/downcast to\
65    /// This list always has at least two elements:
66    /// * id 0: `TypeIdConst::of::<dyn AnyType>`
67    /// * id 1: `TypeIdConst::of::<YourConcreteType>`
68    ///
69    /// Only the rest of the list (aka: from index 2) is ordered,
70    /// so we can run a binary search there if there are many types.
71    fn type_ids(&self) -> &'static [TypeIdConst];
72
73    /// **very unsafe. don't use. internal only. Horror here. go away.**
74    ///
75    /// cast `self` to a trait in the `.type_ids()` list.\
76    /// the pointer to the ref to the type in the list
77    /// is then converted to `usize`.
78    ///
79    /// panics if list length is exceeded.
80    unsafe fn cast_to(&self, trait_num: usize) -> usize;
81    /// **very unsafe. don't use. internal only. Horror here. go away.**
82    ///
83    /// cast `self` to a trait in the `.type_ids()` list.\
84    /// the pointer to the ref to the type in the list
85    /// is then converted to `usize`.
86    ///
87    /// panics if list length is exceeded.
88    unsafe fn cast_to_mut(&mut self, trait_num: usize) -> usize;
89}
90
91/// upcast from the concrete type
92/// (or from any other trait that implements `AnyTrait`) to `&dyn AnyTrait`
93///
94/// Automatically implemented on all types that implement `AnyTrait`
95pub trait AsAnyTrait: AnyTrait {
96    fn as_anytrait(&self) -> &dyn AnyTrait;
97}
98
99// everybody can have the same implementation as the `dyn Any` is always
100// the first type in the list
101impl<T: AnyTrait + ?Sized> AsAnyTrait for T {
102    fn as_anytrait(&self) -> &dyn AnyTrait {
103        unsafe {
104            let raw = self.cast_to(0);
105            union U {
106                ptr: usize,
107                a: *const *const dyn AnyTrait,
108            }
109            let tmp = U { ptr: raw };
110            return &**tmp.a;
111        }
112    }
113}
114
115impl dyn AnyTrait {
116    /// Search the list of possible traits.
117    ///
118    /// If `self` can be cast to the generic parameter,
119    /// return the index of the type in the list
120    #[inline]
121    pub fn trait_idx<T: ?Sized + 'static>(&self) -> Option<usize> {
122        let t = TypeIdConst::of::<T>();
123
124        let all_traits = self.type_ids();
125        if all_traits[0] == t {
126            return Some(0);
127        }
128        if all_traits[1] == t {
129            return Some(1);
130        }
131        let sub_traits = &all_traits[2..];
132
133        // 128: carefully chosen completely at random
134        if sub_traits.len() < 128 {
135            match sub_traits.iter().enumerate().find(|x| *x.1 == t) {
136                Some((idx, _)) => Some(2 + idx),
137                None => None,
138            }
139        } else {
140            match sub_traits.binary_search(&t) {
141                Ok(idx) => Some(2 + idx),
142                Err(_) => None,
143            }
144        }
145    }
146
147    /// Safe cast to reference to a generic type.
148    ///
149    /// Only return Some(...) if it is safe to do so.
150    #[inline]
151    pub fn downcast_ref<T: ?Sized + 'static>(&self) -> Option<&T> {
152        let Some(idx) = self.trait_idx::<T>() else {
153            return None;
154        };
155        #[allow(unsafe_code)]
156        unsafe {
157            #[allow(trivial_casts)]
158            let ta = self.cast_to(idx);
159            union UT<T: ?Sized> {
160                a: usize,
161                d: *const *const T,
162            }
163            let f = UT::<T> { a: ta };
164            let res = *f.d;
165            Some(&*res)
166        }
167    }
168
169    /// Safe cast to mutable reference to a generic type.
170    ///
171    /// Only return Some(...) if it is safe to do so.
172    #[inline]
173    pub fn downcast_mut<T: ?Sized + 'static>(&mut self) -> Option<&mut T> {
174        let Some(idx) = self.trait_idx::<T>() else {
175            return None;
176        };
177        #[allow(unsafe_code)]
178        unsafe {
179            #[allow(trivial_casts)]
180            let ta = self.cast_to_mut(idx);
181            union UT<T: ?Sized> {
182                a: usize,
183                d: *mut *mut T,
184            }
185            let f = UT::<T> { a: ta };
186            let res: *mut T = *f.d;
187            Some(&mut *res)
188        }
189    }
190}