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}