yazi_shared/
ro_cell.rs

1use std::{cell::UnsafeCell, fmt::{self, Display}, mem::MaybeUninit, ops::Deref};
2
3// Read-only cell. It's safe to use this in a static variable, but it's not safe
4// to mutate it. This is useful for storing static data that is expensive to
5// initialize, but is immutable once.
6pub struct RoCell<T> {
7	inner:       UnsafeCell<MaybeUninit<T>>,
8	#[cfg(debug_assertions)]
9	initialized: UnsafeCell<bool>,
10}
11
12unsafe impl<T> Sync for RoCell<T> {}
13
14impl<T> RoCell<T> {
15	#[inline]
16	pub const fn new() -> Self {
17		Self {
18			inner:                                UnsafeCell::new(MaybeUninit::uninit()),
19			#[cfg(debug_assertions)]
20			initialized:                          UnsafeCell::new(false),
21		}
22	}
23
24	#[inline]
25	pub const fn new_const(value: T) -> Self {
26		Self {
27			inner:                                UnsafeCell::new(MaybeUninit::new(value)),
28			#[cfg(debug_assertions)]
29			initialized:                          UnsafeCell::new(true),
30		}
31	}
32
33	#[inline]
34	pub fn init(&self, value: T) {
35		unsafe {
36			#[cfg(debug_assertions)]
37			assert!(!self.initialized.get().replace(true));
38			*self.inner.get() = MaybeUninit::new(value);
39		}
40	}
41
42	#[inline]
43	pub fn with<F>(&self, f: F)
44	where
45		F: FnOnce() -> T,
46	{
47		self.init(f());
48	}
49
50	#[inline]
51	pub fn drop(&self) -> T {
52		unsafe {
53			#[cfg(debug_assertions)]
54			assert!(self.initialized.get().replace(false));
55			self.inner.get().replace(MaybeUninit::uninit()).assume_init()
56		}
57	}
58}
59
60impl<T> Default for RoCell<T> {
61	fn default() -> Self { Self::new() }
62}
63
64impl<T> Deref for RoCell<T> {
65	type Target = T;
66
67	fn deref(&self) -> &Self::Target {
68		unsafe {
69			#[cfg(debug_assertions)]
70			assert!(*self.initialized.get());
71			(*self.inner.get()).assume_init_ref()
72		}
73	}
74}
75
76impl<T> Display for RoCell<T>
77where
78	T: Display,
79{
80	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.deref().fmt(f) }
81}