singlyton/
lib.rs

1#![cfg_attr(not(test), no_std)]
2#![doc = include_str!("../README.md")]
3
4#[cfg(test)]
5mod tests;
6
7mod cell;
8use cell::*;
9pub use cell::{map_ref, map_ref_mut, SinglytonRef, SinglytonRefMut};
10
11#[cfg(debug_assertions)]
12use core::cell::UnsafeCell;
13use core::mem::MaybeUninit;
14
15/// A **thread-unsafe** global singleton.
16///
17/// Using this across threads is undefined behaviour.
18///
19/// # Panics
20///
21/// In debug builds, usage of this abstraction is checked for safety at runtime.
22///
23/// * Using this struct across threads will panic.
24/// * Mixing mutabilty of borrows will panic (this is bypassed if you are using the pointer getters)
25#[repr(transparent)]
26pub struct Singleton<T>(SinglytonCell<T>);
27unsafe impl<T> Sync for Singleton<T> {}
28
29impl<T> Singleton<T> {
30	#[inline]
31	pub const fn new(val: T) -> Self {
32		Self(SinglytonCell::new(val))
33	}
34
35	#[inline]
36	/// Acquires an **immutable reference** to the singleton.
37	///
38	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or if a mutable reference is currently held.
39	pub fn get(&'static self) -> SinglytonRef<T> {
40		self.0.get()
41	}
42
43	#[inline]
44	/// Acquires a **mutable reference** to the singleton.
45	///
46	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or an existing mutable or immutable reference is currently held.
47	pub fn get_mut(&'static self) -> SinglytonRefMut<T> {
48		self.0.get_mut()
49	}
50
51	#[inline]
52	/// Acquires an **immutable pointer** to the singleton.
53	///
54	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or if a mutable reference is currently held.
55	///
56	/// This is unsafe because the returned pointer bypasses any future borrow checking.
57	pub unsafe fn as_ptr(&'static self) -> *const T {
58		&*self.0.get() as *const T
59	}
60
61	#[inline]
62	/// Acquires a **mutable pointer** to the singleton.
63	///
64	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or an existing mutable or immutable reference is currently held.
65	///
66	/// This is unsafe because the returned pointer bypasses any future borrow checking.
67	pub unsafe fn as_mut_ptr(&'static self) -> *mut T {
68		&mut *self.0.get_mut() as *mut T
69	}
70
71	#[inline]
72	/// Replaces the value in the singleton with anew.
73	///
74	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or an existing mutable or immutable reference is currently held.
75	pub fn replace(&'static self, val: T) {
76		*self.0.get_mut() = val;
77	}
78}
79
80/// A **thread-unsafe** global singleton which is initially uninitialized memory.
81///
82/// Using this across threads is undefined behaviour.
83///
84/// # Panics
85///
86/// In debug builds, usage of this abstraction is checked for safety at runtime.
87///
88/// * Using this struct across threads will panic.
89/// * Mixing mutabilty of borrows will panic (this is bypassed if you are using the pointer getters)
90/// * Using this struct before initializing it will panic.
91/// * Initializing the value more than once will panic. Use `replace`
92pub struct SingletonUninit<T> {
93	inner: SinglytonCell<MaybeUninit<T>>,
94
95	#[cfg(debug_assertions)]
96	initialized: UnsafeCell<bool>
97}
98unsafe impl<T> Sync for SingletonUninit<T> {}
99
100impl<T> SingletonUninit<T> {
101	#[inline]
102	pub const fn uninit() -> Self {
103		Self {
104			inner: SinglytonCell::new(MaybeUninit::uninit()),
105
106			#[cfg(debug_assertions)]
107			initialized: UnsafeCell::new(false)
108		}
109	}
110
111	#[inline]
112	pub const fn new(val: T) -> Self {
113		Self {
114			inner: SinglytonCell::new(MaybeUninit::new(val)),
115
116			#[cfg(debug_assertions)]
117			initialized: UnsafeCell::new(true)
118		}
119	}
120
121	#[cfg(debug_assertions)]
122	#[inline(never)]
123	fn uninit_check(&'static self) {
124		if !unsafe { *self.initialized.get() } {
125			panic!("This SingletonUninit has not been initialized yet");
126		}
127	}
128
129	#[cfg(not(debug_assertions))]
130	#[inline(always)]
131	fn uninit_check(&'static self) {}
132
133	#[inline]
134	/// Assumes the memory is **initialized** and acquires an **immutable reference** to the singleton.
135	///
136	/// In debug builds, this will panic if the memory is not initialized, the singleton is mutably accessed from a different thread, or a mutable reference is currently held.
137	pub fn get(&'static self) -> SinglytonRef<T> {
138		self.uninit_check();
139		map_ref(self.inner.get(), |maybe_uninit| unsafe {
140			maybe_uninit.assume_init_ref()
141		})
142	}
143
144	#[inline]
145	/// Acquires a **mutable reference** to the singleton.
146	///
147	/// In debug builds, this will panic if the memory is not initialized, the singleton is mutably accessed from a different thread, or an existing mutable or immutable reference is currently held.
148	pub fn get_mut(&'static self) -> SinglytonRefMut<T> {
149		self.uninit_check();
150		map_ref_mut(self.inner.get_mut(), |maybe_uninit| unsafe {
151			maybe_uninit.assume_init_mut()
152		})
153	}
154
155	#[inline]
156	/// Acquires an **immutable pointer** to the singleton.
157	///
158	/// In debug builds, this will panic if the memory is not initialized, the singleton is mutably accessed from a different thread, or a mutable reference is currently held.
159	///
160	/// This is unsafe because the returned pointer bypasses any future borrow checking.
161	pub unsafe fn as_ptr(&'static self) -> *const T {
162		self.uninit_check();
163		self.inner.get_mut().as_ptr()
164	}
165
166	#[inline]
167	/// Acquires a **mutable pointer** to the singleton.
168	///
169	/// In debug builds, this will panic if the memory is not initialized, the singleton is mutably accessed from a different thread, or an existing mutable or immutable reference is currently held.
170	///
171	/// This is unsafe because the returned pointer bypasses any future borrow checking.
172	pub unsafe fn as_mut_ptr(&'static self) -> *mut T {
173		self.uninit_check();
174		self.inner.get_mut().as_mut_ptr()
175	}
176
177	#[inline]
178	/// Replaces the value in the singleton with anew.
179	///
180	/// In debug builds, this will panic if the memory is not initialized, the singleton is mutably accessed from a different thread, or an existing mutable or immutable reference is currently held.
181	pub fn replace(&'static self, val: T) {
182		self.uninit_check();
183		unsafe {
184			let mut maybe_uninit = self.inner.get_mut();
185
186			core::ptr::drop_in_place(maybe_uninit.as_mut_ptr());
187			maybe_uninit.write(val);
188		}
189	}
190
191	#[inline]
192	#[cfg(debug_assertions)]
193	/// Initializes the memory in the singleton.
194	///
195	/// In debug builds, this will panic if the memory is **already initialized**, the singleton is mutably accessed from a different thread, or an existing mutable or immutable reference is currently held.
196	pub fn init(&'static self, val: T) {
197		unsafe {
198			let ref mut initialized = *self.initialized.get();
199			if *initialized {
200				panic!("This SingletonUninit has already been initialized");
201			}
202
203			self.inner.get_mut().write(val);
204
205			*initialized = true;
206		}
207	}
208
209	#[inline]
210	#[cfg(not(debug_assertions))]
211	/// Initializes the memory in the singleton.
212	///
213	/// In debug builds, this will panic if the memory is **already initialized**, the singleton is mutably accessed from a different thread, or an existing mutable or immutable reference is currently held.
214	pub fn init(&'static self, val: T) {
215		self.inner.get_mut().write(val);
216	}
217}
218
219/// A **thread-unsafe** global singleton containg an `Option<T>`.
220///
221/// All operations (except `as_option` and `as_option_mut`) automatically unwrap and assume the `Option<T>` is `Some(T)` and will panic otherwise.
222///
223/// Using this across threads is undefined behaviour.
224///
225/// # Panics
226///
227/// In debug builds, usage of this abstraction is checked for safety at runtime.
228///
229/// * Using this struct across threads will panic.
230/// * Mixing mutabilty of borrows will panic (this is bypassed if you are using the pointer getters)
231#[repr(transparent)]
232pub struct SingletonOption<T>(SinglytonCell<Option<T>>);
233unsafe impl<T> Sync for SingletonOption<T> {}
234
235impl<T> SingletonOption<T> {
236	#[inline]
237	pub const fn new() -> Self {
238		Self(SinglytonCell::new(None))
239	}
240
241	#[inline]
242	pub const fn new_some(val: T) -> Self {
243		Self(SinglytonCell::new(Some(val)))
244	}
245
246	#[inline]
247	/// Acquires an **immutable reference** to the inner `Option<T>`.
248	///
249	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or if a mutable reference is currently held.
250	pub fn as_option(&'static self) -> SinglytonRef<Option<T>> {
251		self.0.get()
252	}
253
254	#[inline]
255	/// Acquires a **mutable reference** to the inner `Option<T>`.
256	///
257	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or an existing mutable or immutable reference is currently held.
258	pub fn as_option_mut(&'static self) -> SinglytonRefMut<Option<T>> {
259		self.0.get_mut()
260	}
261
262	#[inline]
263	/// Acquires an **immutable pointer** to the inner Option<T>.
264	///
265	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or if a mutable reference is currently held.
266	///
267	/// This is unsafe because the returned pointer bypasses any future borrow checking.
268	pub unsafe fn as_option_ptr(&'static self) -> *const Option<T> {
269		&*self.0.get_unchecked() as *const Option<T>
270	}
271
272	#[inline]
273	/// Acquires a **mutable pointer** to the inner Option<T>.
274	///
275	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or an existing mutable or immutable reference is currently held.
276	///
277	/// This is unsafe because the returned pointer bypasses any future borrow checking.
278	pub unsafe fn as_option_mut_ptr(&'static self) -> *mut Option<T> {
279		&mut *self.0.get_mut_unchecked() as *mut Option<T>
280	}
281
282	#[inline]
283	/// Acquires an **immutable reference** to the singleton.
284	///
285	/// Panics if the singleton is `None`.
286	///
287	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or if a mutable reference is currently held.
288	pub fn get(&'static self) -> SinglytonRef<T> {
289		map_ref(self.0.get(), |opt| opt.as_ref().unwrap())
290	}
291
292	#[inline]
293	/// Acquires a **mutable reference** to the singleton.
294	///
295	/// Panics if the singleton is `None`.
296	///
297	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or an existing mutable or immutable reference is currently held.
298	pub fn get_mut(&'static self) -> SinglytonRefMut<T> {
299		map_ref_mut(self.0.get_mut(), |opt| opt.as_mut().unwrap())
300	}
301
302	#[inline]
303	/// Replaces the value in the singleton with anew.
304	///
305	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or an existing mutable or immutable reference is currently held.
306	pub fn replace(&'static self, val: T) {
307		self.0.get_mut().replace(val);
308	}
309
310	#[inline]
311	/// Takes the value out of the singleton.
312	///
313	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or an existing mutable or immutable reference is currently held.
314	pub fn take(&'static self) -> Option<T> {
315		self.0.get_mut().take()
316	}
317
318	#[inline]
319	/// Tests if the singleton is `Some(T)`.
320	///
321	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or if a mutable reference is currently held.
322	pub fn is_some(&'static self) -> bool {
323		self.0.get().is_some()
324	}
325
326	#[inline]
327	/// Tests if the singleton is `None`.
328	///
329	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or if a mutable reference is currently held.
330	pub fn is_none(&'static self) -> bool {
331		self.0.get().is_none()
332	}
333}
334
335/// A **thread-unsafe** global singleton containg an `Option<T>`.
336///
337/// All operations (except `as_option` and `as_option_mut`) automatically unwrap **without checking if the Option<T> is Some(T) in release builds** and will lead to undefined behaviour otherwise.
338///
339/// Using this across threads is undefined behaviour.
340///
341/// # Panics
342///
343/// In debug builds, usage of this abstraction is checked for safety at runtime.
344///
345/// * Using this struct across threads will panic.
346/// * Mixing mutabilty of borrows will panic (this is bypassed if you are using the pointer getters)
347#[repr(transparent)]
348pub struct SingletonOptionUnchecked<T>(SinglytonCell<Option<T>>);
349unsafe impl<T> Sync for SingletonOptionUnchecked<T> {}
350
351impl<T> SingletonOptionUnchecked<T> {
352	#[inline]
353	pub const fn new() -> Self {
354		Self(SinglytonCell::new(None))
355	}
356
357	#[inline]
358	pub const fn new_some(val: T) -> Self {
359		Self(SinglytonCell::new(Some(val)))
360	}
361
362	#[inline]
363	/// Acquires an **immutable reference** to the inner `Option<T>`.
364	///
365	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or if a mutable reference is currently held.
366	pub fn as_option(&'static self) -> SinglytonRef<Option<T>> {
367		self.0.get()
368	}
369
370	#[inline]
371	/// Acquires a **mutable reference** to the inner `Option<T>`.
372	///
373	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or an existing mutable or immutable reference is currently held.
374	pub fn as_option_mut(&'static self) -> SinglytonRefMut<Option<T>> {
375		self.0.get_mut()
376	}
377
378	#[inline]
379	/// Acquires an **immutable pointer** to the inner Option<T>.
380	///
381	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or if a mutable reference is currently held.
382	///
383	/// This is unsafe because the returned pointer bypasses any future borrow checking.
384	pub unsafe fn as_option_ptr(&'static self) -> *const Option<T> {
385		&*self.0.get_unchecked() as *const Option<T>
386	}
387
388	#[inline]
389	/// Acquires a **mutable pointer** to the inner Option<T>.
390	///
391	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or an existing mutable or immutable reference is currently held.
392	///
393	/// This is unsafe because the returned pointer bypasses any future borrow checking.
394	pub unsafe fn as_option_mut_ptr(&'static self) -> *mut Option<T> {
395		&mut *self.0.get_mut_unchecked() as *mut Option<T>
396	}
397
398	#[inline]
399	/// Acquires an **immutable reference** to the singleton.
400	///
401	/// Panics if the singleton is `None`.
402	///
403	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or if a mutable reference is currently held.
404	pub unsafe fn get(&'static self) -> SinglytonRef<T> {
405		map_ref(self.0.get(), |opt| {
406			#[cfg(debug_assertions)] {
407				opt.as_ref().unwrap()
408			}
409			#[cfg(not(debug_assertions))] {
410				opt.as_ref().unwrap_unchecked()
411			}
412		})
413	}
414
415	#[inline]
416	/// Acquires a **mutable reference** to the singleton.
417	///
418	/// Panics if the singleton is `None`.
419	///
420	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or an existing mutable or immutable reference is currently held.
421	pub unsafe fn get_mut(&'static self) -> SinglytonRefMut<T> {
422		map_ref_mut(self.0.get_mut(), |opt| {
423			#[cfg(debug_assertions)] {
424				opt.as_mut().unwrap()
425			}
426			#[cfg(not(debug_assertions))] {
427				opt.as_mut().unwrap_unchecked()
428			}
429		})
430	}
431
432	#[inline]
433	/// Replaces the value in the singleton with anew.
434	///
435	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or an existing mutable or immutable reference is currently held.
436	pub fn replace(&'static self, val: T) {
437		self.0.get_mut().replace(val);
438	}
439
440	#[inline]
441	/// Takes the value out of the singleton.
442	///
443	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or an existing mutable or immutable reference is currently held.
444	pub fn take(&'static self) -> Option<T> {
445		self.0.get_mut().take()
446	}
447
448	#[inline]
449	/// Tests if the singleton is `Some(T)`.
450	///
451	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or if a mutable reference is currently held.
452	pub fn is_some(&'static self) -> bool {
453		self.0.get().is_some()
454	}
455
456	#[inline]
457	/// Tests if the singleton is `None`.
458	///
459	/// In debug builds, this will panic if the singleton is mutably accessed from a different thread or if a mutable reference is currently held.
460	pub fn is_none(&'static self) -> bool {
461		self.0.get().is_none()
462	}
463}