1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! 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<u32>,
//! }
//!
//! impl Oracle {
//!     pub fn get_answer(&self) -> &u32 {
//!         self.answer.get(|| think()) // think() is called only once
//!     }
//! }
//! # fn think() -> u32 {42}
//! ```
//!
use std::cell::UnsafeCell;
use std::sync::Mutex;

/// Wrapper type for lazy initialization
pub struct LazyOnce<T> {
    cell: Mutex<UnsafeCell<Option<T>>>,
}

impl<T> LazyOnce<T> {
    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().unwrap();
        unsafe {
            (*cell.get()).get_or_insert_with(f)
        }
    }

    /// Get cached value or `None` if the cell is still empty
    ///
    /// Does not wait on the lock if the cell is being filled in
    #[must_use]
    pub fn try_get(&self) -> Option<&T> {
        match self.cell.try_lock() {
            Ok(cell) => {
                unsafe {
                    (*cell.get()).as_ref()
                }
            },
            Err(_) => 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().unwrap();
        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().unwrap();
        unsafe {
            (*cell.get()).as_ref().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<T> {
        let cell = self.cell.lock().unwrap();
        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_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.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: Sync>(_: T)  {}
    is_sync(&LazyOnce::<u8>::new());
}