1#![no_std]
12
13mod check;
14
15pub use check::{AtomicCheck, BasicCheck, CheckedPolicy, Lifecycle, NoCheck, Policy};
16
17use core::cell::UnsafeCell;
18use core::convert::Infallible;
19use core::marker::PhantomData;
20use core::mem::MaybeUninit;
21use core::ops;
22use core::pin::Pin;
23
24use pin_init::PinInit;
25
26pub struct LazyInit<T, Check: Policy = BasicCheck> {
33 value: UnsafeCell<MaybeUninit<T>>,
34 check: Check::State,
35 _check: PhantomData<Check>,
36}
37
38impl<T, Check: Policy> LazyInit<T, Check> {
39 pub const fn uninit() -> Self {
41 Self {
42 value: UnsafeCell::new(MaybeUninit::uninit()),
43 check: Check::UNINIT_STATE,
44 _check: PhantomData,
45 }
46 }
47
48 pub unsafe fn init(&'static self, value: T) {
61 let _ = Check::init_with(&self.check, || -> Result<(), Infallible> {
62 unsafe {
63 (*self.value.get()).write(value);
64 }
65 Ok(())
66 });
67 }
68
69 pub unsafe fn init_pin<E>(self: Pin<&'static Self>, init: impl PinInit<T, E>) -> Result<(), E> {
83 let slot = unsafe { (*self.value.get()).as_mut_ptr() };
84 Check::init_with(&self.check, || unsafe { init.__pinned_init(slot) })
86 }
87
88 #[inline(always)]
95 pub const unsafe fn get_unchecked(&self) -> &T {
96 unsafe { (*self.value.get()).assume_init_ref() }
97 }
98}
99
100impl<T, Check: CheckedPolicy> LazyInit<T, Check> {
101 #[inline(always)]
109 pub fn get(&self) -> &T {
110 Check::assert_initialized(&self.check);
111 unsafe { self.get_unchecked() }
113 }
114}
115
116impl<T, Check: CheckedPolicy> ops::Deref for LazyInit<T, Check> {
117 type Target = T;
118
119 #[inline(always)]
120 fn deref(&self) -> &Self::Target {
121 self.get()
122 }
123}
124
125unsafe impl<T: Send, Check: Policy> Send for LazyInit<T, Check> where Check::State: Send {}
127
128unsafe impl<T: Sync, Check: Policy> Sync for LazyInit<T, Check> {}
132
133#[cfg(test)]
134mod tests {
135 use pin_init::{pin_data, pin_init};
136
137 use super::*;
138
139 #[test]
140 fn basic_check() {
141 static LAZY: LazyInit<i32, BasicCheck> = LazyInit::uninit();
142 unsafe {
143 LAZY.init(42);
144 }
145 assert_eq!(*LAZY.get(), 42);
146 assert_eq!(*LAZY, 42);
147 assert_eq!(unsafe { *LAZY.get_unchecked() }, 42);
148 }
149
150 #[test]
151 fn no_check() {
152 static LAZY: LazyInit<i32, NoCheck> = LazyInit::uninit();
153 unsafe {
154 LAZY.init(42);
155 }
156 assert_eq!(unsafe { *LAZY.get_unchecked() }, 42);
157 }
158
159 #[test]
160 #[should_panic(expected = "LazyInit: accessed before initialization")]
161 fn basic_uninit_get_panics() {
162 static LAZY: LazyInit<i32, BasicCheck> = LazyInit::uninit();
163 let _ = LAZY.get();
164 }
165
166 #[test]
167 #[should_panic(expected = "LazyInit: already initialized")]
168 fn basic_double_init_panics() {
169 static LAZY: LazyInit<i32, BasicCheck> = LazyInit::uninit();
170 unsafe {
171 LAZY.init(1);
172 LAZY.init(2);
173 }
174 }
175
176 #[test]
177 fn atomic_check() {
178 static LAZY: LazyInit<&'static str, AtomicCheck> = LazyInit::uninit();
179 unsafe {
180 LAZY.init("hello world");
181 }
182 assert_eq!(*LAZY.get(), "hello world");
183 }
184
185 #[test]
186 #[should_panic(expected = "LazyInit: accessed before initialization")]
187 fn atomic_uninit_get_panics() {
188 static LAZY: LazyInit<i32, AtomicCheck> = LazyInit::uninit();
189 let _ = LAZY.get();
190 }
191
192 #[test]
193 #[should_panic(expected = "LazyInit: already initialized")]
194 fn atomic_double_init_panics() {
195 static LAZY: LazyInit<i32, AtomicCheck> = LazyInit::uninit();
196 unsafe {
197 LAZY.init(1);
198 LAZY.init(2);
199 }
200 }
201
202 #[test]
203 fn init_pin() {
204 #[pin_data]
205 struct PinnedStruct {
206 val: u32,
207 }
208
209 static LAZY: LazyInit<PinnedStruct, BasicCheck> = LazyInit::uninit();
210 let pinned = Pin::static_ref(&LAZY);
211 unsafe {
212 pinned.init_pin(pin_init!(PinnedStruct { val: 123 })).unwrap();
213 }
214 assert_eq!(pinned.get().val, 123);
215 }
216}