//! Wrapper type for lazy initialization //! //! Store "immutable" cell that computes its value once, when it's first accessed. //! Allows lazy initialization as an implementation detail, without need to expose any mutable methods. //! //! It's like `lazy_static`, but not static. //! It's like `std::sync::Once`, but holds a value. //! //! It's thread-safe (`Send` and `Sync`). //! //! ```rust //! use lazyonce::LazyOnce; //! struct Oracle { //! answer: LazyOnce, //! } //! //! impl Oracle { //! pub fn get_answer(&self) -> &u32 { //! self.answer.get(|| think()) // think() is called only once //! } //! } //! # fn think() -> u32 {42} //! ``` //! use parking_lot::Mutex; use std::cell::UnsafeCell; use std::time::Duration; /// Wrapper type for lazy initialization pub struct LazyOnce { cell: Mutex>>, } impl LazyOnce { pub fn new() -> Self { Self { cell: Mutex::new(UnsafeCell::new(None)) } } /// Get cached value, or if no value has been cached yet, compute it using the callback function. /// /// If the callback panics, any use of the cell will panic, too. #[must_use] pub fn get(&self, f: impl FnOnce() -> T) -> &T { let cell = self.cell.lock(); unsafe { (*cell.get()).get_or_insert_with(f) } } /// Get cached value or `None` if the cell is still empty /// /// Does not wait if the cell is being filled in #[must_use] pub fn try_get(&self) -> Option<&T> { match self.cell.try_lock() { Some(cell) => unsafe { (*cell.get()).as_ref() }, None => None, } } /// Get cached value or `None` if the cell is still empty /// /// It waits for the cell to be filled in only for a limited time, and falls back to `None` on timeout #[must_use] pub fn try_get_for(&self, duration: Duration, f: impl FnOnce() -> T) -> Option<&T> { match self.cell.try_lock_for(duration) { Some(cell) => unsafe { Some((*cell.get()).get_or_insert_with(f)) }, None => None, } } /// Get mutable reference to the cached value, or if no value has been cached yet, compute it using the callback function. #[must_use] pub fn get_mut(&mut self, f: impl FnOnce() -> T) -> &mut T { let cell = self.cell.lock(); unsafe { (*cell.get()).get_or_insert_with(f) } } /// Assume the value has already been computed. Crash if the cell is empty. /// /// # Panics /// /// If `get()` hasn't been called yet. pub fn expect(&self, s: &str) -> &T { let cell = self.cell.lock(); unsafe { (*cell.get()).as_ref().expect(s) } } /// Assume the value has already been computed. Crash if the cell is empty. /// /// # Panics /// /// If `get()` hasn't been called yet. pub fn expect_mut(&mut self, s: &str) -> &mut T { let cell = self.cell.lock(); unsafe { (*cell.get()).as_mut().expect(s) } } /// Return computed value or `None` if the cell is empty. /// /// Unlike `try_get` this returns an owned value, permanently "unwrapping" the cell. #[must_use] pub fn into_inner(self) -> Option { let cell = self.cell.lock(); unsafe { (*cell.get()).take() } } } #[test] fn test_get() { let l = LazyOnce::new(); assert_eq!(None, l.try_get()); assert_eq!(1, *l.get(|| 1u8)); assert_eq!(1, *l.get(|| 2u8)); assert_eq!(1, *l.try_get().unwrap()); assert_eq!(1, *l.get(|| 3u8)); assert_eq!(1, *l.expect("set")); } #[test] fn test_recursive() { let l = LazyOnce::new(); let _ = *l.get(|| { assert!(l.try_get().is_none()); 1u8 }); } #[test] fn test_recursive_timeout() { let l = LazyOnce::new(); let _ = *l.get(|| { assert!(l.try_get_for(Duration::from_millis(10), || 1).is_none()); 1u8 }); } #[test] fn test_mut() { let mut l = LazyOnce::new(); assert_eq!(1, *l.get_mut(|| 1u8)); assert_eq!(1, *l.get(|| 2u8)); assert_eq!(1, *l.get_mut(|| 3u8)); *l.get_mut(|| 3u8) = 4; assert_eq!(4, *l.get_mut(|| 3u8)); assert_eq!(4, *l.try_get().unwrap()); assert_eq!(4, *l.expect("set")); assert_eq!(4, *l.expect_mut("set")); assert_eq!(4, l.into_inner().unwrap()); } #[test] fn test_send_and_sync() { let l = LazyOnce::new(); ::std::thread::spawn(move || { let _ = l.get(|| "hi"); }); fn is_sync(_: T) {} is_sync(&LazyOnce::::new()); }