xref: /relibc/ralloc/src/lazy_init.rs (revision 998377c6b486f141b1744fc7da7fb5f5089ab16b)
1 //! `LazyStatic` like initialization.
2 
3 /// The initialization state
4 enum State<F, T> {
5     /// The data is uninitialized, initialization is pending.
6     ///
7     /// The inner closure contains the initialization function.
8     Uninitialized(F),
9     /// The data is initialized, and ready for use.
10     Initialized(T),
11 }
12 
13 /// A lazily initialized container.
14 ///
15 /// This container starts out simply containing an initializer (i.e., a function to construct the
16 /// value in question). When the value is requested, the initializer runs.
17 pub struct LazyInit<F, T> {
18     /// The internal state.
19     state: State<F, T>,
20 }
21 
22 impl<F: FnMut() -> T, T> LazyInit<F, T> {
23     /// Create a new to-be-initialized container.
24     ///
25     /// The closure will be executed when initialization is required, and is guaranteed to be
26     /// executed at most once.
27     #[inline]
28     pub const fn new(init: F) -> LazyInit<F, T> {
29         LazyInit {
30             state: State::Uninitialized(init),
31         }
32     }
33 
34     /// Get a mutable reference to the inner value.
35     ///
36     /// If it is uninitialize, it will be initialized and then returned.
37     #[inline]
38     pub fn get(&mut self) -> &mut T {
39         let inner;
40 
41         match self.state {
42             State::Initialized(ref mut x) => return x,
43             State::Uninitialized(ref mut f) => inner = f(),
44         }
45 
46         self.state = State::Initialized(inner);
47 
48         if let State::Initialized(ref mut x) = self.state {
49             x
50         } else {
51             // TODO: Find a better way to deal with this case.
52             unreachable!();
53         }
54     }
55 
56     /// Get the inner of the container.
57     ///
58     /// This won't mutate the container itself, since it consumes it. The initializer will (if
59     /// necessary) be called and the result returned. If already initialized, the inner value will
60     /// be moved out and returned.
61     pub fn into_inner(self) -> T {
62         match self.state {
63             State::Initialized(x) => x,
64             State::Uninitialized(mut f) => f(),
65         }
66     }
67 }
68 
69 #[cfg(test)]
70 mod test {
71     use super::*;
72 
73     use core::cell::Cell;
74 
75     #[test]
76     fn test_init() {
77         let mut lazy = LazyInit::new(|| 300);
78 
79         assert_eq!(*lazy.get(), 300);
80         *lazy.get() = 400;
81         assert_eq!(*lazy.get(), 400);
82     }
83 
84     #[test]
85     fn test_laziness() {
86         let is_called = Cell::new(false);
87         let mut lazy = LazyInit::new(|| is_called.set(true));
88         assert!(!is_called.get());
89         lazy.get();
90         assert!(is_called.get());
91     }
92 }
93