Skip to main content

miniextendr_api/
externalptr_std.rs

1//! `TypedExternal` implementations for standard library types.
2//!
3//! This module provides `TypedExternal` implementations for common std types,
4//! allowing them to be stored in `ExternalPtr<T>` without manual implementation.
5//!
6//! # Generic Types
7//!
8//! For generic types like `Vec<T>`, the type name does not include the type parameter
9//! (e.g., `Vec<i32>` and `Vec<String>` both have type name "Vec"). This means type
10//! checking at the R level won't distinguish between different instantiations.
11//! If you need stricter type safety, create a newtype wrapper and derive `ExternalPtr`.
12
13use crate::externalptr::TypedExternal;
14
15/// Implement TypedExternal for a concrete type.
16/// For standard library types, we use the simple name for both display and ID.
17macro_rules! impl_te {
18    ($ty:ty, $name:literal) => {
19        impl TypedExternal for $ty {
20            const TYPE_NAME: &'static str = $name;
21            const TYPE_NAME_CSTR: &'static [u8] = concat!($name, "\0").as_bytes();
22            const TYPE_ID_CSTR: &'static [u8] = concat!("std::", $name, "\0").as_bytes();
23        }
24    };
25}
26
27/// Implement TypedExternal for a generic type with 'static bounds.
28/// For standard library types, we prefix the ID with "std::" for clarity.
29macro_rules! impl_te_generic {
30    (<$($g:ident),+> $ty:ty, $name:literal) => {
31        impl<$($g: 'static),+> TypedExternal for $ty {
32            const TYPE_NAME: &'static str = $name;
33            const TYPE_NAME_CSTR: &'static [u8] = concat!($name, "\0").as_bytes();
34            const TYPE_ID_CSTR: &'static [u8] = concat!("std::", $name, "\0").as_bytes();
35        }
36    };
37    (<$($g:ident : $bound:path),+> $ty:ty, $name:literal) => {
38        impl<$($g: $bound + 'static),+> TypedExternal for $ty {
39            const TYPE_NAME: &'static str = $name;
40            const TYPE_NAME_CSTR: &'static [u8] = concat!($name, "\0").as_bytes();
41            const TYPE_ID_CSTR: &'static [u8] = concat!("std::", $name, "\0").as_bytes();
42        }
43    };
44}
45
46// region: Primitives
47
48impl_te!(bool, "bool");
49impl_te!(char, "char");
50impl_te!(i8, "i8");
51impl_te!(i16, "i16");
52impl_te!(i32, "i32");
53impl_te!(i64, "i64");
54impl_te!(i128, "i128");
55impl_te!(isize, "isize");
56impl_te!(u8, "u8");
57impl_te!(u16, "u16");
58impl_te!(u32, "u32");
59impl_te!(u64, "u64");
60impl_te!(u128, "u128");
61impl_te!(usize, "usize");
62impl_te!(f32, "f32");
63impl_te!(f64, "f64");
64// endregion
65
66// region: Strings
67
68impl_te!(String, "String");
69impl_te!(std::ffi::CString, "CString");
70impl_te!(std::ffi::OsString, "OsString");
71impl_te!(std::path::PathBuf, "PathBuf");
72// endregion
73
74// region: Collections
75
76impl_te_generic!(<T> Vec<T>, "Vec");
77impl_te_generic!(<T> std::collections::VecDeque<T>, "VecDeque");
78impl_te_generic!(<T> std::collections::LinkedList<T>, "LinkedList");
79impl_te_generic!(<T> std::collections::BinaryHeap<T>, "BinaryHeap");
80impl_te_generic!(<K, V> std::collections::HashMap<K, V>, "HashMap");
81impl_te_generic!(<K, V> std::collections::BTreeMap<K, V>, "BTreeMap");
82impl_te_generic!(<T> std::collections::HashSet<T>, "HashSet");
83impl_te_generic!(<T> std::collections::BTreeSet<T>, "BTreeSet");
84// endregion
85
86// region: Smart Pointers
87
88impl_te_generic!(<T> Box<T>, "Box");
89
90// Box<[T]> is a special case - it's a fat pointer (Sized) wrapping a DST.
91// Unlike Box<T> where T: Sized, Box<[T]> can store dynamically-sized slices.
92// This is useful for ALTREP when you want fixed-size heap allocation without
93// Vec's capacity overhead.
94impl<T: 'static> TypedExternal for Box<[T]> {
95    const TYPE_NAME: &'static str = "BoxSlice";
96    const TYPE_NAME_CSTR: &'static [u8] = b"BoxSlice\0";
97    const TYPE_ID_CSTR: &'static [u8] = b"std::BoxSlice\0";
98}
99
100// Cow<'a, [T]> is a fat pointer like Box<[T]> — needs explicit impl.
101impl<T: Clone + 'static> TypedExternal for std::borrow::Cow<'static, [T]> {
102    const TYPE_NAME: &'static str = "CowSlice";
103    const TYPE_NAME_CSTR: &'static [u8] = b"CowSlice\0";
104    const TYPE_ID_CSTR: &'static [u8] = b"std::CowSlice\0";
105}
106
107impl_te_generic!(<T> std::rc::Rc<T>, "Rc");
108impl_te_generic!(<T> std::sync::Arc<T>, "Arc");
109impl_te_generic!(<T> std::cell::Cell<T>, "Cell");
110impl_te_generic!(<T> std::cell::RefCell<T>, "RefCell");
111impl_te_generic!(<T> std::cell::UnsafeCell<T>, "UnsafeCell");
112impl_te_generic!(<T> std::sync::Mutex<T>, "Mutex");
113impl_te_generic!(<T> std::sync::RwLock<T>, "RwLock");
114impl_te_generic!(<T> std::sync::OnceLock<T>, "OnceLock");
115impl_te_generic!(<T> std::pin::Pin<T>, "Pin");
116// ManuallyDrop<T> shares T's type symbols, allowing ExternalPtr<ManuallyDrop<T>>
117// to interoperate with ExternalPtr<T>. This is safe because ManuallyDrop<T> is
118// #[repr(transparent)] and has identical memory layout to T.
119impl<T: TypedExternal> TypedExternal for std::mem::ManuallyDrop<T> {
120    const TYPE_NAME: &'static str = T::TYPE_NAME;
121    const TYPE_NAME_CSTR: &'static [u8] = T::TYPE_NAME_CSTR;
122    const TYPE_ID_CSTR: &'static [u8] = T::TYPE_ID_CSTR;
123}
124impl_te_generic!(<T> std::mem::MaybeUninit<T>, "MaybeUninit");
125impl_te_generic!(<T> std::marker::PhantomData<T>, "PhantomData");
126// endregion
127
128// region: Option / Result
129
130impl_te_generic!(<T> Option<T>, "Option");
131impl_te_generic!(<T, E> Result<T, E>, "Result");
132// endregion
133
134// region: Ranges
135
136impl_te_generic!(<T> std::ops::Range<T>, "Range");
137impl_te_generic!(<T> std::ops::RangeInclusive<T>, "RangeInclusive");
138impl_te_generic!(<T> std::ops::RangeFrom<T>, "RangeFrom");
139impl_te_generic!(<T> std::ops::RangeTo<T>, "RangeTo");
140impl_te_generic!(<T> std::ops::RangeToInclusive<T>, "RangeToInclusive");
141impl_te!(std::ops::RangeFull, "RangeFull");
142// endregion
143
144// region: I/O
145
146impl_te!(std::fs::File, "File");
147impl_te_generic!(<R: std::io::Read> std::io::BufReader<R>, "BufReader");
148impl_te_generic!(<W: std::io::Write> std::io::BufWriter<W>, "BufWriter");
149impl_te_generic!(<T> std::io::Cursor<T>, "Cursor");
150// endregion
151
152// region: Time
153
154impl_te!(std::time::Duration, "Duration");
155impl_te!(std::time::Instant, "Instant");
156impl_te!(std::time::SystemTime, "SystemTime");
157// endregion
158
159// region: Networking
160
161impl_te!(std::net::TcpStream, "TcpStream");
162impl_te!(std::net::TcpListener, "TcpListener");
163impl_te!(std::net::UdpSocket, "UdpSocket");
164impl_te!(std::net::IpAddr, "IpAddr");
165impl_te!(std::net::Ipv4Addr, "Ipv4Addr");
166impl_te!(std::net::Ipv6Addr, "Ipv6Addr");
167impl_te!(std::net::SocketAddr, "SocketAddr");
168impl_te!(std::net::SocketAddrV4, "SocketAddrV4");
169impl_te!(std::net::SocketAddrV6, "SocketAddrV6");
170// endregion
171
172// region: Threading
173
174impl_te!(std::thread::Thread, "Thread");
175impl_te_generic!(<T> std::thread::JoinHandle<T>, "JoinHandle");
176impl_te_generic!(<T> std::sync::mpsc::Sender<T>, "Sender");
177impl_te_generic!(<T> std::sync::mpsc::SyncSender<T>, "SyncSender");
178impl_te_generic!(<T> std::sync::mpsc::Receiver<T>, "Receiver");
179impl_te!(std::sync::Barrier, "Barrier");
180impl_te!(std::sync::BarrierWaitResult, "BarrierWaitResult");
181// endregion
182
183// region: Atomics
184
185impl_te!(std::sync::atomic::AtomicBool, "AtomicBool");
186impl_te!(std::sync::atomic::AtomicI8, "AtomicI8");
187impl_te!(std::sync::atomic::AtomicI16, "AtomicI16");
188impl_te!(std::sync::atomic::AtomicI32, "AtomicI32");
189impl_te!(std::sync::atomic::AtomicI64, "AtomicI64");
190impl_te!(std::sync::atomic::AtomicIsize, "AtomicIsize");
191impl_te!(std::sync::atomic::AtomicU8, "AtomicU8");
192impl_te!(std::sync::atomic::AtomicU16, "AtomicU16");
193impl_te!(std::sync::atomic::AtomicU32, "AtomicU32");
194impl_te!(std::sync::atomic::AtomicU64, "AtomicU64");
195impl_te!(std::sync::atomic::AtomicUsize, "AtomicUsize");
196// endregion
197
198// region: Numeric wrappers
199
200// Note: NonZero<T> requires T: ZeroablePrimitive (sealed trait), so we use aliases
201impl_te!(std::num::NonZeroI8, "NonZero");
202impl_te!(std::num::NonZeroI16, "NonZero");
203impl_te!(std::num::NonZeroI32, "NonZero");
204impl_te!(std::num::NonZeroI64, "NonZero");
205impl_te!(std::num::NonZeroI128, "NonZero");
206impl_te!(std::num::NonZeroIsize, "NonZero");
207impl_te!(std::num::NonZeroU8, "NonZero");
208impl_te!(std::num::NonZeroU16, "NonZero");
209impl_te!(std::num::NonZeroU32, "NonZero");
210impl_te!(std::num::NonZeroU64, "NonZero");
211impl_te!(std::num::NonZeroU128, "NonZero");
212impl_te!(std::num::NonZeroUsize, "NonZero");
213impl_te_generic!(<T> std::num::Wrapping<T>, "Wrapping");
214impl_te_generic!(<T> std::num::Saturating<T>, "Saturating");
215// endregion
216
217// region: Tuples (1-12 elements)
218
219impl_te_generic!(<A> (A,), "Tuple1");
220impl_te_generic!(<A, B> (A, B), "Tuple2");
221impl_te_generic!(<A, B, C> (A, B, C), "Tuple3");
222impl_te_generic!(<A, B, C, D> (A, B, C, D), "Tuple4");
223impl_te_generic!(<A, B, C, D, E> (A, B, C, D, E), "Tuple5");
224impl_te_generic!(<A, B, C, D, E, F> (A, B, C, D, E, F), "Tuple6");
225impl_te_generic!(<A, B, C, D, E, F, G> (A, B, C, D, E, F, G), "Tuple7");
226impl_te_generic!(<A, B, C, D, E, F, G, H> (A, B, C, D, E, F, G, H), "Tuple8");
227impl_te_generic!(<A, B, C, D, E, F, G, H, I> (A, B, C, D, E, F, G, H, I), "Tuple9");
228impl_te_generic!(<A, B, C, D, E, F, G, H, I, J> (A, B, C, D, E, F, G, H, I, J), "Tuple10");
229impl_te_generic!(<A, B, C, D, E, F, G, H, I, J, K> (A, B, C, D, E, F, G, H, I, J, K), "Tuple11");
230impl_te_generic!(<A, B, C, D, E, F, G, H, I, J, K, L> (A, B, C, D, E, F, G, H, I, J, K, L), "Tuple12");
231// endregion
232
233// region: Arrays (const generic)
234
235impl<T: 'static, const N: usize> TypedExternal for [T; N] {
236    const TYPE_NAME: &'static str = "Array";
237    const TYPE_NAME_CSTR: &'static [u8] = b"Array\0";
238    const TYPE_ID_CSTR: &'static [u8] = b"std::Array\0";
239}
240// endregion
241
242// region: Static slices
243//
244// `&'static [T]` is Sized (it's a fat pointer: ptr + len, 2 words) and satisfies
245// 'static, so it can be stored directly in ExternalPtr.
246//
247// Use cases:
248// - Const arrays: `static DATA: [i32; 5] = [1, 2, 3, 4, 5]; altrep(&DATA)`
249// - Leaked data: `let leaked: &'static [i32] = Box::leak(vec![1, 2, 3].into_boxed_slice());`
250// - Memory-mapped files with 'static lifetime
251//
252// Note: The data must genuinely live forever. If using Box::leak, the memory
253// is never freed (intentional memory leak for the lifetime of the process).
254
255impl<T: 'static> TypedExternal for &'static [T] {
256    const TYPE_NAME: &'static str = "StaticSlice";
257    const TYPE_NAME_CSTR: &'static [u8] = b"StaticSlice\0";
258    const TYPE_ID_CSTR: &'static [u8] = b"std::StaticSlice\0";
259}
260
261impl<T: 'static> TypedExternal for &'static mut [T] {
262    const TYPE_NAME: &'static str = "StaticMutSlice";
263    const TYPE_NAME_CSTR: &'static [u8] = b"StaticMutSlice\0";
264    const TYPE_ID_CSTR: &'static [u8] = b"std::StaticMutSlice\0";
265}
266// endregion