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}