conquer_once/lazy.rs
1//! Generic definition and implementation of the [`Lazy`] type.
2
3use core::{borrow::Borrow, convert::AsRef, fmt, mem::ManuallyDrop, ops::Deref, ptr};
4
5use crate::cell::{Block, OnceCell, Unblock};
6
7/// A type for lazy initialization of e.g. global static variables, which
8/// provides the same functionality as the `lazy_static!` macro.
9pub struct Lazy<T, B, F = fn() -> T> {
10 /// The cell storing the lazily initialized value.
11 cell: OnceCell<T, B>,
12 /// The initialization function or closure;
13 /// this is wrapped in a [`ManuallyDrop`] so that [`FnOnce`] closures can
14 /// be used as well.
15 init: ManuallyDrop<F>,
16}
17
18impl<T, B, F> Lazy<T, B, F> {
19 /// Creates a new uninitialized [`Lazy`] with the given `init` closure.
20 ///
21 /// # Examples
22 ///
23 /// The `init` argument can be a simple function pointer or any [`FnOnce`]
24 /// closure.
25 ///
26 /// ```
27 /// # #[cfg(feature = "std")]
28 /// use conquer_once::Lazy;
29 /// # #[cfg(not(feature = "std"))]
30 /// # use conquer_once::spin::Lazy;
31 ///
32 /// static LAZY_1: Lazy<Vec<i32>> = Lazy::new(|| vec![1, 2, 3, 4, 5]);
33 /// static LAZY_2: Lazy<Vec<i32>> = Lazy::new(Vec::<i32>::new);
34 /// ```
35 #[inline]
36 pub const fn new(init: F) -> Self {
37 Self { cell: OnceCell::uninit(), init: ManuallyDrop::new(init) }
38 }
39
40 /// Returns `true` if the [`Lazy`] has been successfully initialized.
41 #[inline]
42 pub fn is_initialized(lazy: &Self) -> bool {
43 lazy.cell.is_initialized()
44 }
45
46 /// Returns `true` if the [`Lazy`] has been poisoned.
47 #[inline]
48 pub fn is_poisoned(lazy: &Self) -> bool {
49 lazy.cell.is_poisoned()
50 }
51}
52
53impl<T, B, F> Lazy<T, B, F>
54where
55 B: Block,
56 F: FnOnce() -> T,
57{
58 /// Returns a reference to the already initialized inner value or
59 /// initializes it first.
60 ///
61 /// This has the same effect as using the [`Deref`] operator on a [`Lazy`].
62 ///
63 /// # Panics
64 ///
65 /// This method panics if the `init` procedure specified during construction
66 /// panics or if the [`Lazy`] is poisoned.
67 #[inline]
68 pub fn get_or_init(lazy: &Self) -> &T {
69 lazy.cell.get_or_init(|| {
70 // SAFETY: this (outer) closure is only called once and `init` is
71 // never dropped, so it will never be touched again
72 let func = unsafe { ptr::read(&*lazy.init) };
73 func()
74 })
75 }
76}
77
78impl<T, B, F> AsRef<T> for Lazy<T, B, F>
79where
80 B: Block,
81 F: FnOnce() -> T,
82{
83 #[inline]
84 fn as_ref(&self) -> &T {
85 Lazy::get_or_init(self)
86 }
87}
88
89impl<T, B, F> Borrow<T> for Lazy<T, B, F>
90where
91 B: Block,
92 F: FnOnce() -> T,
93{
94 #[inline]
95 fn borrow(&self) -> &T {
96 Lazy::get_or_init(self)
97 }
98}
99
100impl<T, B, F> fmt::Debug for Lazy<T, B, F>
101where
102 T: fmt::Debug,
103 B: Unblock,
104 F: FnOnce() -> T,
105{
106 #[inline]
107 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108 fmt::Debug::fmt(&self.cell, f)
109 }
110}
111
112impl<T, B, F> fmt::Display for Lazy<T, B, F>
113where
114 T: fmt::Display,
115 B: Block,
116 F: FnOnce() -> T,
117{
118 #[inline]
119 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120 fmt::Display::fmt(Self::get_or_init(self), f)
121 }
122}
123
124impl<T, B, F> Deref for Lazy<T, B, F>
125where
126 B: Block,
127 F: FnOnce() -> T,
128{
129 type Target = T;
130
131 #[inline]
132 fn deref(&self) -> &Self::Target {
133 Lazy::get_or_init(self)
134 }
135}