rrtk/
reference.rs

1// SPDX-License-Identifier: BSD-3-Clause
2// Copyright 2024 UxuginPython
3//![`Reference`] is a container holding an enum with variants containing different kinds of
4//!references, the availability of some of which depends on crate features. [`Reference`] is borrowed like
5//!a [`RefCell`]. This module contains it and its related types. [`Reference`] is also reexported at
6//!the crate level.
7use crate::*;
8#[cfg(feature = "alloc")]
9use core::cell::{Ref, RefMut};
10#[cfg(feature = "std")]
11use std::sync::{MutexGuard, RwLockReadGuard, RwLockWriteGuard};
12///An immutable borrow of an RRTK [`Reference`], similar to [`Ref`] for a [`RefCell`].
13///
14///This is marked as non-exhaustive because some variants are only available with some features.
15///This means that if you write a `match` without all the features enabled, it won't cover all the
16///variants if another crate in the tree enables more features. This is a problem because features
17///are additive, so it is marked as non-exhaustive to remedy this.
18#[non_exhaustive]
19pub enum Borrow<'a, T: ?Sized> {
20    ///A raw immutable pointer.
21    Ptr(*const T, PhantomData<&'a ()>),
22    ///An immutable borrow of an `Rc<RefCell<T>>`.
23    #[cfg(feature = "alloc")]
24    RefCellRef(Ref<'a, T>),
25    ///An [`RwLockReadGuard`].
26    #[cfg(feature = "std")]
27    RwLockReadGuard(RwLockReadGuard<'a, T>),
28    ///A [`MutexGuard`].
29    #[cfg(feature = "std")]
30    MutexGuard(MutexGuard<'a, T>),
31}
32impl<T: ?Sized> Deref for Borrow<'_, T> {
33    type Target = T;
34    fn deref(&self) -> &T {
35        match self {
36            Self::Ptr(ptr, _) => unsafe { &**ptr },
37            #[cfg(feature = "alloc")]
38            Self::RefCellRef(ref_cell_ref) => ref_cell_ref,
39            #[cfg(feature = "std")]
40            Self::RwLockReadGuard(rw_lock_read_guard) => rw_lock_read_guard,
41            #[cfg(feature = "std")]
42            Self::MutexGuard(mutex_guard) => mutex_guard,
43        }
44    }
45}
46///A mutable borrow of an RRTK [`Reference`], similar to [`RefMut`] for a [`RefCell`].
47///
48///This is marked as non-exhaustive because some variants are only available with some features.
49///This means that if you write a `match` without all the features enabled, it won't cover all the
50///variants if another crate in the tree enables more features. This is a problem because features
51///are additive, so it is marked as non-exhaustive to remedy this.
52#[non_exhaustive]
53pub enum BorrowMut<'a, T: ?Sized> {
54    ///A raw mutable pointer.
55    Ptr(*mut T, PhantomData<&'a ()>),
56    ///A mutable borrow of an `Rc<RefCell<T>>`.
57    #[cfg(feature = "alloc")]
58    RefCellRefMut(RefMut<'a, T>),
59    ///An [`RwLockWriteGuard`].
60    #[cfg(feature = "std")]
61    RwLockWriteGuard(RwLockWriteGuard<'a, T>),
62    ///A [`MutexGuard`].
63    #[cfg(feature = "std")]
64    MutexGuard(MutexGuard<'a, T>),
65}
66impl<T: ?Sized> Deref for BorrowMut<'_, T> {
67    type Target = T;
68    fn deref(&self) -> &T {
69        match self {
70            Self::Ptr(ptr, _) => unsafe { &**ptr },
71            #[cfg(feature = "alloc")]
72            Self::RefCellRefMut(ref_cell_ref_mut) => ref_cell_ref_mut,
73            #[cfg(feature = "std")]
74            Self::RwLockWriteGuard(rw_lock_write_guard) => rw_lock_write_guard,
75            #[cfg(feature = "std")]
76            Self::MutexGuard(mutex_guard) => mutex_guard,
77        }
78    }
79}
80impl<T: ?Sized> DerefMut for BorrowMut<'_, T> {
81    fn deref_mut(&mut self) -> &mut T {
82        match self {
83            Self::Ptr(ptr, _) => unsafe { &mut **ptr },
84            #[cfg(feature = "alloc")]
85            Self::RefCellRefMut(ref_cell_ref_mut) => ref_cell_ref_mut,
86            #[cfg(feature = "std")]
87            Self::RwLockWriteGuard(rw_lock_write_guard) => rw_lock_write_guard,
88            #[cfg(feature = "std")]
89            Self::MutexGuard(mutex_guard) => mutex_guard,
90        }
91    }
92}
93///A special enum with variants for different kinds of references depending on your platform and
94///code structure. (Some variants are alloc- or std-only.) It is usually contained in a
95///[`Reference`], which is a safe wrapper. You should generally use [`Reference`] over
96///[`ReferenceUnsafe`] unless you specifically need to match against it, probably for some form of
97///type conversion.
98///
99///This is marked as non-exhaustive because some variants are only available with some features.
100///This means that if you write a `match` without all the features enabled, it won't cover all the
101///variants if another crate in the tree enables more features. This is a problem because features
102///are additive, so it is marked as non-exhaustive to remedy this.
103#[non_exhaustive]
104pub enum ReferenceUnsafe<T: ?Sized> {
105    ///A raw mutable pointer. This is a useful variant if you are not multithreading and you want
106    ///to avoid dynamic allocation. Making the target static is recommended.
107    Ptr(*mut T),
108    ///An `Rc<RefCell<T>>`.
109    #[cfg(feature = "alloc")]
110    RcRefCell(Rc<RefCell<T>>),
111    ///A raw immutable pointer to an [`RwLock<T>`]. Making the [`RwLock`] itself static is recommended.
112    #[cfg(feature = "std")]
113    PtrRwLock(*const RwLock<T>),
114    ///A raw pointer to a [`Mutex<T>`]. Making the [`Mutex`] itself static is recommended.
115    #[cfg(feature = "std")]
116    PtrMutex(*const Mutex<T>),
117    ///An `Arc<RwLock<T>>`.
118    #[cfg(feature = "std")]
119    ArcRwLock(Arc<RwLock<T>>),
120    ///An `Arc<Mutex<T>>`.
121    #[cfg(feature = "std")]
122    ArcMutex(Arc<Mutex<T>>),
123}
124impl<T: ?Sized> ReferenceUnsafe<T> {
125    ///Create a [`ReferenceUnsafe`] from a raw mutable pointer. This is useful if you are not
126    ///multithreading and you want to avoid dynamic allocation. Making the target static is
127    ///recommended.
128    pub const unsafe fn from_ptr(ptr: *mut T) -> Self {
129        Self::Ptr(ptr)
130    }
131    ///Create a [`ReferenceUnsafe`] from an `Rc<RefCell<T>>`.
132    #[cfg(feature = "alloc")]
133    pub const fn from_rc_ref_cell(rc_ref_cell: Rc<RefCell<T>>) -> Self {
134        Self::RcRefCell(rc_ref_cell)
135    }
136    ///Create a [`ReferenceUnsafe`] from a `*const RwLock<T>`. Making the [`RwLock`] itself static is
137    ///recommended.
138    #[cfg(feature = "std")]
139    pub const unsafe fn from_ptr_rw_lock(ptr_rw_lock: *const RwLock<T>) -> Self {
140        Self::PtrRwLock(ptr_rw_lock)
141    }
142    ///Create a [`ReferenceUnsafe`] from a `*const Mutex<T>`. Making the [`Mutex`] itself static is
143    ///recommended.
144    #[cfg(feature = "std")]
145    pub const unsafe fn from_ptr_mutex(ptr_mutex: *const Mutex<T>) -> Self {
146        Self::PtrMutex(ptr_mutex)
147    }
148    ///Create a new [`ReferenceUnsafe`] from an `Arc<RwLock<T>>`.
149    #[cfg(feature = "std")]
150    pub const fn from_arc_rw_lock(arc_rw_lock: Arc<RwLock<T>>) -> Self {
151        Self::ArcRwLock(arc_rw_lock)
152    }
153    ///Create a [`ReferenceUnsafe`] from an `Arc<Mutex<T>>`.
154    #[cfg(feature = "std")]
155    pub const fn from_arc_mutex(arc_mutex: Arc<Mutex<T>>) -> Self {
156        Self::ArcMutex(arc_mutex)
157    }
158    ///Immutably borrow the [`ReferenceUnsafe`] like a [`RefCell`]. This is unsafe because of the
159    ///potential for a dereference of the borrow to dereference a null or freed raw pointer.
160    pub unsafe fn borrow(&self) -> Borrow<'_, T> {
161        match self {
162            Self::Ptr(ptr) => Borrow::Ptr(*ptr, PhantomData),
163            #[cfg(feature = "alloc")]
164            Self::RcRefCell(rc_ref_cell) => Borrow::RefCellRef(rc_ref_cell.borrow()),
165            #[cfg(feature = "std")]
166            Self::PtrRwLock(ptr_rw_lock) => unsafe {
167                Borrow::RwLockReadGuard(
168                    (**ptr_rw_lock)
169                        .read()
170                        .expect("RRTK Reference borrow failed to get RwLock read lock"),
171                )
172            },
173            #[cfg(feature = "std")]
174            Self::PtrMutex(ptr_mutex) => unsafe {
175                Borrow::MutexGuard(
176                    (**ptr_mutex)
177                        .lock()
178                        .expect("RRTK Reference borrow failed to get Mutex lock"),
179                )
180            },
181            #[cfg(feature = "std")]
182            Self::ArcRwLock(arc_rw_lock) => Borrow::RwLockReadGuard(
183                arc_rw_lock
184                    .read()
185                    .expect("RRTK Reference borrow failed to get RwLock read lock"),
186            ),
187            #[cfg(feature = "std")]
188            Self::ArcMutex(arc_mutex) => Borrow::MutexGuard(
189                arc_mutex
190                    .lock()
191                    .expect("RRTK Reference borrow failed to get Mutex lock"),
192            ),
193        }
194    }
195    ///Mutably borrow the [`ReferenceUnsafe`] like a [`RefCell`]. Thus is unsafe because of the
196    ///potential for a dereference of the borrow to dereference a null or freed raw pointer.
197    pub unsafe fn borrow_mut(&self) -> BorrowMut<'_, T> {
198        match self {
199            Self::Ptr(ptr) => BorrowMut::Ptr(*ptr, PhantomData),
200            #[cfg(feature = "alloc")]
201            Self::RcRefCell(rc_ref_cell) => BorrowMut::RefCellRefMut(rc_ref_cell.borrow_mut()),
202            #[cfg(feature = "std")]
203            Self::PtrRwLock(ptr_rw_lock) => unsafe {
204                BorrowMut::RwLockWriteGuard(
205                    (**ptr_rw_lock)
206                        .write()
207                        .expect("RRTK Reference mutable borrow failed to get RwLock write lock"),
208                )
209            },
210            #[cfg(feature = "std")]
211            Self::PtrMutex(ptr_mutex) => unsafe {
212                BorrowMut::MutexGuard(
213                    (**ptr_mutex)
214                        .lock()
215                        .expect("RRTK Reference mutable borrow failed to get Mutex lock"),
216                )
217            },
218            #[cfg(feature = "std")]
219            Self::ArcRwLock(arc_rw_lock) => BorrowMut::RwLockWriteGuard(
220                arc_rw_lock
221                    .write()
222                    .expect("RRTK Reference mutable borrow failed to get RwLock write lock"),
223            ),
224            #[cfg(feature = "std")]
225            Self::ArcMutex(arc_mutex) => BorrowMut::MutexGuard(
226                arc_mutex
227                    .lock()
228                    .expect("RRTK Reference mutable borrow failed to get Mutex lock"),
229            ),
230        }
231    }
232}
233impl<T: ?Sized> Clone for ReferenceUnsafe<T> {
234    fn clone(&self) -> Self {
235        match self {
236            Self::Ptr(ptr) => Self::Ptr(*ptr),
237            #[cfg(feature = "alloc")]
238            Self::RcRefCell(rc_ref_cell) => Self::RcRefCell(Rc::clone(&rc_ref_cell)),
239            #[cfg(feature = "std")]
240            Self::PtrRwLock(ptr_rw_lock) => Self::PtrRwLock(*ptr_rw_lock),
241            #[cfg(feature = "std")]
242            Self::PtrMutex(ptr_mutex) => Self::PtrMutex(*ptr_mutex),
243            #[cfg(feature = "std")]
244            Self::ArcRwLock(arc_rw_lock) => Self::ArcRwLock(Arc::clone(&arc_rw_lock)),
245            #[cfg(feature = "std")]
246            Self::ArcMutex(arc_mutex) => Self::ArcMutex(Arc::clone(&arc_mutex)),
247        }
248    }
249}
250impl<T: ?Sized> From<Reference<T>> for ReferenceUnsafe<T> {
251    fn from(was: Reference<T>) -> Self {
252        was.into_inner()
253    }
254}
255///A container privately holding an enum with variants containing different kinds of references,
256///the availability of some of which depends on crate features. [`Reference`] is borrowed like a [`RefCell`].
257///It is also reexported at the crate level.
258#[repr(transparent)]
259pub struct Reference<T: ?Sized>(ReferenceUnsafe<T>);
260impl<T: ?Sized> Reference<T> {
261    ///Create a [`Reference`] from a raw mutable pointer. This is useful if you are not
262    ///multithreading and you want to avoid dynamic allocation. Making the target static is
263    ///recommended. The [`static_reference!`] macro is a convenient way of making an object
264    ///static and getting a `Reference` of the raw pointer variant to it. Because the object is
265    ///guaranteed to be static, it can be called without an unsafe block.
266    pub const unsafe fn from_ptr(ptr: *mut T) -> Self {
267        Self(ReferenceUnsafe::from_ptr(ptr))
268    }
269    ///Create a [`Reference`] from an `Rc<RefCell<T>>`. The [`rc_ref_cell_reference`] function is a
270    ///convenient way of putting an object in an `Rc<RefCell<T>>` and getting a [`Reference`] of this
271    ///variant to it.
272    #[cfg(feature = "alloc")]
273    pub const fn from_rc_ref_cell(rc_ref_cell: Rc<RefCell<T>>) -> Self {
274        Self(ReferenceUnsafe::from_rc_ref_cell(rc_ref_cell))
275    }
276    ///Create a [`Reference`] from a `*const RwLock<T>`. Making the [`RwLock`] itself static is
277    ///recommended. The [`static_rw_lock_reference!`] macro is a convenient way of putting
278    ///an object in a static [`RwLock`] and getting a `Reference` of this variant to it.
279    #[cfg(feature = "std")]
280    pub const unsafe fn from_ptr_rw_lock(ptr_rw_lock: *const RwLock<T>) -> Self {
281        Self(ReferenceUnsafe::from_ptr_rw_lock(ptr_rw_lock))
282    }
283    ///Create a [`Reference`] from a `*const Mutex<T>`. Making the [`Mutex`] itself static is
284    ///recommended. The [`static_mutex_reference!`] macro is a convenient way of putting an object in
285    ///a static [`Mutex`] and getting a [`Reference`] of this variant to it.
286    #[cfg(feature = "std")]
287    pub const unsafe fn from_ptr_mutex(ptr_mutex: *const Mutex<T>) -> Self {
288        Self(ReferenceUnsafe::from_ptr_mutex(ptr_mutex))
289    }
290    ///Create a [`Reference`] from an `Arc<RwLock<T>>`. The [`arc_rw_lock_reference`] function is a
291    ///convenient way of putting an object in an [`Arc<RwLock>`] and getting a [`Reference`] of this
292    ///variant to it.
293    #[cfg(feature = "std")]
294    pub const fn from_arc_rw_lock(arc_rw_lock: Arc<RwLock<T>>) -> Self {
295        Self(ReferenceUnsafe::from_arc_rw_lock(arc_rw_lock))
296    }
297    ///Create a [`Reference`] from an `Arc<Mutex<T>>`. The [`arc_mutex_reference`] function is a
298    ///convenient way of putting an object in an `Arc<Mutex>` and getting a [`Reference`] of this
299    ///variant to it.
300    #[cfg(feature = "std")]
301    pub const fn from_arc_mutex(arc_mutex: Arc<Mutex<T>>) -> Self {
302        Self(ReferenceUnsafe::from_arc_mutex(arc_mutex))
303    }
304    ///Get the inner [`ReferenceUnsafe`].
305    pub fn into_inner(self) -> ReferenceUnsafe<T> {
306        self.0
307    }
308    ///Immutably borrow the [`Reference`] like a [`RefCell`].
309    pub fn borrow(&self) -> Borrow<'_, T> {
310        unsafe { self.0.borrow() }
311    }
312    ///Mutably borrow the [`Reference`] like a [`RefCell`].
313    pub fn borrow_mut(&self) -> BorrowMut<'_, T> {
314        unsafe { self.0.borrow_mut() }
315    }
316}
317impl<T: ?Sized> Clone for Reference<T> {
318    fn clone(&self) -> Self {
319        Self(self.0.clone())
320    }
321}
322///If you have a `Reference<Foo>` where `Foo` implements the `Bar` trait, you may end up wanting a
323///`Reference<dyn Bar>`. To convert it, you would do this:
324///```
325///# use rrtk::*;
326///struct Foo;
327///impl Foo {
328///    fn foo_func(&self) {}
329///}
330///trait Bar {
331///    fn bar_func(&self) {}
332///}
333///impl Bar for Foo {}
334///let ref_foo = static_reference!(Foo, Foo);
335///ref_foo.borrow().foo_func();
336///ref_foo.borrow().bar_func();
337///let ref_dyn_bar = to_dyn!(Bar, ref_foo);
338///ref_dyn_bar.borrow().bar_func();
339///```
340///
341///The documentation shows `rrtk::to_dyn` and `rrtk::reference::to_dyn` separately. These are the
342///same macro exported in two different places. These paths point to the same code in RRTK. Rust's
343///scoping rules for macros are a bit odd, but you should be able to use `rrtk::to_dyn` and
344///`rrtk::reference::to_dyn` interchangably.
345#[macro_export]
346macro_rules! to_dyn {
347    ($trait_:path, $was:expr) => {{
348        #[cfg(feature = "alloc")]
349        extern crate alloc;
350        #[allow(unreachable_patterns)]
351        match $was.into_inner() {
352            reference::ReferenceUnsafe::Ptr(ptr) => unsafe {
353                Reference::from_ptr(ptr as *mut dyn $trait_)
354            },
355            #[cfg(feature = "alloc")]
356            reference::ReferenceUnsafe::RcRefCell(rc_ref_cell) => Reference::from_rc_ref_cell(
357                rc_ref_cell as alloc::rc::Rc<core::cell::RefCell<dyn $trait_>>,
358            ),
359            #[cfg(feature = "std")]
360            reference::ReferenceUnsafe::PtrRwLock(ptr_rw_lock) => unsafe {
361                Reference::from_ptr_rw_lock(ptr_rw_lock as *const std::sync::RwLock<dyn $trait_>)
362            },
363            _ => unimplemented!(),
364        }
365    }};
366}
367pub use to_dyn;
368///Create a new `Rc<RefCell>` of something and return a [`Reference`] to it. Because of how [`Rc`]
369///works, it won't be dropped until the last clone of the `Reference` is. This is reexported at the
370///crate level.
371#[cfg(feature = "alloc")]
372pub fn rc_ref_cell_reference<T>(was: T) -> Reference<T> {
373    Reference::from_rc_ref_cell(Rc::new(RefCell::new(was)))
374}
375///Create a static of something and return a `Ptr`-variant [`Reference`] to it. This contains a raw
376///mutable pointer. It will never use-after-free because its target is static, but be careful if
377///you're doing multiprocessing where multiple things could mutate it at once.
378///
379///The documentation shows `rrtk::static_reference` and `rrtk::reference::static_reference` separately. These are the
380///same macro exported in two different places. These paths point to the same code in RRTK. Rust's
381///scoping rules for macros are a bit odd, but you should be able to use `rrtk::static_reference` and
382///`rrtk::reference::static_reference` interchangably.
383#[macro_export]
384macro_rules! static_reference {
385    ($type_: ty, $was: expr) => {{
386        static mut WAS: $type_ = $was;
387        unsafe { Reference::from_ptr(core::ptr::addr_of_mut!(WAS)) }
388    }};
389}
390pub use static_reference;
391///Create a static [`RwLock`] of something and return a `PtrRwLock`-variant [`Reference`] to it.
392///
393///The documentation shows `rrtk::static_rw_lock_reference` and `rrtk::reference::static_rw_lock_reference` separately. These are the
394///same macro exported in two different places. These paths point to the same code in RRTK. Rust's
395///scoping rules for macros are a bit odd, but you should be able to use `rrtk::static_rw_lock_reference` and
396///`rrtk::reference::static_rw_lock_reference` interchangably.
397#[cfg(feature = "std")]
398#[macro_export]
399macro_rules! static_rw_lock_reference {
400    ($type_: ty, $was: expr) => {{
401        static WAS: std::sync::RwLock<$type_> = std::sync::RwLock::new($was);
402        unsafe { Reference::from_ptr_rw_lock(core::ptr::addr_of!(WAS)) }
403    }};
404}
405#[cfg(feature = "std")]
406pub use static_rw_lock_reference;
407///Create a new static [`Mutex`] of something and return a `PtrMutex`-variant [`Reference`] to it.
408///
409///The documentation shows `rrtk::static_mutex_reference` and `rrtk::reference::static_mutex_reference` separately. These are the
410///same macro exported in two different places. These paths point to the same code in RRTK. Rust's
411///scoping rules for macros are a bit odd, but you should be able to use `rrtk::static_mutex_reference` and
412///`rrtk::reference::static_mutex_reference` interchangably.
413#[cfg(feature = "std")]
414#[macro_export]
415macro_rules! static_mutex_reference {
416    ($type_: ty, $was: expr) => {{
417        static WAS: std::sync::Mutex<$type_> = std::sync::Mutex::new($was);
418        unsafe { Reference::from_ptr_mutex(core::ptr::addr_of!(WAS)) }
419    }};
420}
421///Create a new `Arc<RwLock>` of something and return a [`Reference`] to it. Because of how [`Arc`] and
422///[`Rc`], its single-threaded counterpart, work, it won't be dropped until the last clone of the
423///[`Reference`] is. This is reexported at the crate level.
424#[cfg(feature = "std")]
425pub fn arc_rw_lock_reference<T>(was: T) -> Reference<T> {
426    Reference::from_arc_rw_lock(Arc::new(RwLock::new(was)))
427}
428#[cfg(feature = "std")]
429pub use static_mutex_reference;
430///Create a new `Arc<Mutex>` of something and return a [`Reference`] to it. Because of how [`Arc`] and
431///[`Rc`], its single-threaded counterpart, work, it won't be dropped until the last clone of the
432///[`Reference`] is. This is reexported at the crate level.
433#[cfg(feature = "std")]
434pub fn arc_mutex_reference<T>(was: T) -> Reference<T> {
435    Reference::from_arc_mutex(Arc::new(Mutex::new(was)))
436}