stack_any/
lib.rs

1//! # stack-any
2//!
3//! A library that provides a type that owns same size type on the stack for type erasure.
4//!
5//! ## Usage
6//!
7//! ```
8//! let mut stacks = [
9//!     stack_any::stack_any!(Vec<i32>, vec![]),
10//!     stack_any::stack_any!(Vec<char>, vec![]),
11//! ];
12//!
13//! stacks[0].downcast_mut::<Vec<i32>>().unwrap().push(5);
14//! stacks[1].downcast_mut::<Vec<char>>().unwrap().push('x');
15//!
16//! assert_eq!(stacks[0].downcast_ref(), Some(&vec![5]));
17//! assert_eq!(stacks[1].downcast_ref(), Some(&vec!['x']));
18//! ```
19
20#![cfg_attr(not(feature = "std"), no_std)]
21
22/// A convertible type that owns a stack allocation of `N` size.
23#[derive(Debug)]
24pub struct StackAny<const N: usize> {
25    type_id: core::any::TypeId,
26    bytes: [core::mem::MaybeUninit<u8>; N],
27    drop_fn: fn(*mut core::mem::MaybeUninit<u8>) -> (),
28}
29
30impl<const N: usize> StackAny<N> {
31    /// Allocates N-size memory on the stack and then places `value` into it.
32    /// Returns None if `T` size is larger than N.
33    ///
34    /// # Examples
35    ///
36    /// ```
37    /// let five = stack_any::StackAny::<{ std::mem::size_of::<i32>() }>::try_new(5);
38    /// ```
39    pub fn try_new<T>(value: T) -> Option<Self>
40    where
41        T: core::any::Any,
42    {
43        let type_id = core::any::TypeId::of::<T>();
44        let size = core::mem::size_of::<T>();
45
46        if N < size {
47            return None;
48        }
49
50        let mut bytes = [core::mem::MaybeUninit::uninit(); N];
51
52        let src = &value as *const _ as *const _;
53        let dst = bytes.as_mut_ptr();
54        unsafe { core::ptr::copy_nonoverlapping(src, dst, size) };
55
56        let drop_fn = |ptr| unsafe { core::ptr::drop_in_place(ptr as *mut T) };
57        core::mem::forget(value);
58
59        Some(Self {
60            type_id,
61            bytes,
62            drop_fn,
63        })
64    }
65
66    /// Attempt to return reference to the inner value as a concrete type.
67    /// Returns None if `T` is not equal to contained value type.
68    ///
69    /// # Examples
70    ///
71    /// ```
72    /// let five = stack_any::stack_any!(i32, 5);
73    /// assert_eq!(five.downcast_ref::<i32>(), Some(&5));
74    /// assert_eq!(five.downcast_ref::<i64>(), None);
75    /// ```
76    pub fn downcast_ref<T>(&self) -> Option<&T>
77    where
78        T: core::any::Any,
79    {
80        if core::any::TypeId::of::<T>() != self.type_id {
81            return None;
82        }
83
84        let ptr = self.bytes.as_ptr();
85        Some(unsafe { &*(ptr as *const T) })
86    }
87
88    /// Attempt to return mutable reference to the inner value as a concrete type.
89    /// Returns None if `T` is not equal to contained value type.
90    ///
91    /// # Examples
92    ///
93    /// ```
94    /// let mut five = stack_any::stack_any!(i32, 5);
95    /// assert_eq!(five.downcast_mut::<i32>(), Some(&mut 5));
96    /// assert_eq!(five.downcast_mut::<i64>(), None);
97    /// ```
98    pub fn downcast_mut<T>(&mut self) -> Option<&mut T>
99    where
100        T: core::any::Any,
101    {
102        if core::any::TypeId::of::<T>() != self.type_id {
103            return None;
104        }
105
106        let ptr = self.bytes.as_mut_ptr();
107        Some(unsafe { &mut *(ptr as *mut T) })
108    }
109
110    /// Attempt to downcast the stack to a concrete type.
111    /// Returns None if `T` is not equal to contained value type.
112    ///
113    /// # Examples
114    ///
115    /// ```
116    /// let five = stack_any::stack_any!(i32, 5);
117    /// assert_eq!(five.downcast::<i32>(), Some(5));
118    /// ```
119    pub fn downcast<T>(mut self) -> Option<T>
120    where
121        T: core::any::Any,
122    {
123        if core::any::TypeId::of::<T>() != self.type_id {
124            return None;
125        }
126
127        self.drop_fn = |_| {};
128
129        let ptr = self.bytes.as_ptr();
130        Some(unsafe { core::ptr::read(ptr as *const T) })
131    }
132}
133
134impl<const N: usize> Drop for StackAny<N> {
135    fn drop(&mut self) {
136        (self.drop_fn)(self.bytes.as_mut_ptr());
137    }
138}
139
140/// Allocates memory on the stack and then places value based on given type and value.
141///
142/// # Examples
143///
144/// ```
145/// let five = stack_any::stack_any!(i32, 5);
146/// ```
147#[macro_export]
148macro_rules! stack_any {
149    ($type:ty, $init:expr) => {
150        $crate::StackAny::<{ std::mem::size_of::<$type>() }>::try_new::<$type>($init).unwrap()
151    };
152}