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}