simple_lazy/lib.rs
1use std::cell::Cell;
2use std::ops::Deref;
3use std::panic::RefUnwindSafe;
4use std::sync::OnceLock;
5
6/// A value which is initialized on the first access.
7///
8/// This type is thread-safe and can be used in statics.
9///
10/// # Example
11///
12/// ```
13/// use std::collections::HashMap;
14///
15/// use simple_lazy::Lazy;
16///
17/// static HASHMAP: Lazy<HashMap<i32, String>> = Lazy::new(|| {
18/// println!("initializing");
19/// let mut m = HashMap::new();
20/// m.insert(13, "Spica".to_string());
21/// m.insert(74, "Hoyten".to_string());
22/// m
23/// });
24///
25/// fn main() {
26/// println!("ready");
27/// std::thread::spawn(|| {
28/// println!("{:?}", HASHMAP.get(&13));
29/// }).join().unwrap();
30/// println!("{:?}", HASHMAP.get(&74));
31///
32/// // Prints:
33/// // ready
34/// // initializing
35/// // Some("Spica")
36/// // Some("Hoyten")
37/// }
38/// ```
39pub struct Lazy<T, F = fn() -> T> {
40 once: OnceLock<T>,
41 init: Cell<Option<F>>,
42}
43
44// We never create a `&F` from a `&Lazy<T, F>` so it is fine to not impl
45// `Sync` for `F`. We do create a `&mut Option<F>` in `force`, but this is
46// properly synchronized, so it only happens once so it also does not
47// contribute to this impl.
48unsafe impl<T, F: Send> Sync for Lazy<T, F> where OnceLock<T>: Sync {}
49// auto-derived `Send` impl is OK.
50
51impl<T, F: RefUnwindSafe> RefUnwindSafe for Lazy<T, F> where OnceLock<T>: RefUnwindSafe {}
52
53impl<T, F> Lazy<T, F> {
54 pub const fn new(init: F) -> Self {
55 Lazy {
56 once: OnceLock::new(),
57 init: Cell::new(Some(init)),
58 }
59 }
60}
61
62impl<T, F: FnOnce() -> T> Lazy<T, F> {
63 /// Forces the evaluation of this lazy value and
64 /// returns a reference to the result. This is equivalent
65 /// to the `Deref` impl, but is explicit.
66 ///
67 /// # Example
68 /// ```
69 /// use simple_lazy::Lazy;
70 ///
71 /// let lazy = Lazy::new(|| 92);
72 ///
73 /// assert_eq!(Lazy::force(&lazy), &92);
74 /// assert_eq!(&*lazy, &92);
75 /// ```
76 pub fn force(this: &Lazy<T, F>) -> &T {
77 this.once.get_or_init(|| match this.init.take() {
78 Some(f) => f(),
79 None => panic!("Lazy instance has previously been poisoned"),
80 })
81 }
82
83 /// Gets the reference to the result of this lazy value if
84 /// it was initialized, otherwise returns `None`.
85 ///
86 /// # Example
87 /// ```
88 /// use simple_lazy::Lazy;
89 ///
90 /// let lazy = Lazy::new(|| 92);
91 ///
92 /// assert_eq!(Lazy::get(&lazy), None);
93 /// assert_eq!(&*lazy, &92);
94 /// assert_eq!(Lazy::get(&lazy), Some(&92));
95 /// ```
96 pub fn get(this: &Lazy<T, F>) -> Option<&T> {
97 this.once.get()
98 }
99}
100
101impl<T, F: FnOnce() -> T> Deref for Lazy<T, F> {
102 type Target = T;
103 fn deref(&self) -> &T {
104 Lazy::force(self)
105 }
106}
107
108impl<T: Default> Default for Lazy<T> {
109 /// Creates a new lazy value using `Default` as the initializing function.
110 fn default() -> Lazy<T> {
111 Lazy::new(T::default)
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 fn assert_traits<T: Send + Sync + RefUnwindSafe>() {}
120
121 #[test]
122 fn test_lazy() {
123 assert_traits::<Lazy<String>>();
124 }
125}