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
//! Extension functions for [`std::sync::OnceLock`] / [`std::cell::OnceCell`]
//!
//! This adds polyfills for functionality in `lazycell` that is not stable within `std`.
pub trait OnceExt {
type T;
/// This might run `f` multiple times if different threads start initializing at once.
fn try_borrow_with<F, E>(&self, f: F) -> Result<&Self::T, E>
where
F: FnOnce() -> Result<Self::T, E>;
/// This might run `f` multiple times if different threads start initializing at once.
fn try_borrow_mut_with<F, E>(&mut self, f: F) -> Result<&mut Self::T, E>
where
F: FnOnce() -> Result<Self::T, E>;
fn replace(&mut self, new_value: Self::T) -> Option<Self::T>;
fn filled(&self) -> bool;
}
impl<T> OnceExt for std::sync::OnceLock<T> {
type T = T;
fn try_borrow_with<F, E>(&self, f: F) -> Result<&T, E>
where
F: FnOnce() -> Result<T, E>,
{
if let Some(value) = self.get() {
return Ok(value);
}
// This is not how the unstable `OnceLock::get_or_try_init` works. That only starts `f` if
// no other `f` is executing and the value is not initialized. However, correctly implementing that is
// hard (one has properly handle panics in `f`) and not doable with the stable API of `OnceLock`.
let value = f()?;
// Another thread might have initialized `self` since we checked that `self.get()` returns `None`. If this is the case, `self.set()`
// returns an error. We ignore it and return the value set by the other
// thread.
let _ = self.set(value);
Ok(self.get().unwrap())
}
fn try_borrow_mut_with<F, E>(&mut self, f: F) -> Result<&mut T, E>
where
F: FnOnce() -> Result<T, E>,
{
let value = if let Some(value) = self.take() {
value
} else {
// This is not how the unstable `OnceLock::get_or_try_init` works. That only starts `f` if
// no other `f` is executing and the value is not initialized. However, correctly implementing that is
// hard (one has properly handle panics in `f`) and not doable with the stable API of `OnceLock`.
f()?
};
// Another thread might have initialized `self` since we checked that `self.get()` returns `None`. If this is the case, `self.set()`
// returns an error. We ignore it and return the value set by the other
// thread.
let _ = self.set(value);
Ok(self.get_mut().unwrap())
}
fn replace(&mut self, new_value: T) -> Option<T> {
if let Some(value) = self.get_mut() {
Some(std::mem::replace(value, new_value))
} else {
let result = self.set(new_value);
assert!(result.is_ok());
None
}
}
fn filled(&self) -> bool {
self.get().is_some()
}
}
impl<T> OnceExt for std::cell::OnceCell<T> {
type T = T;
fn try_borrow_with<F, E>(&self, f: F) -> Result<&T, E>
where
F: FnOnce() -> Result<T, E>,
{
if let Some(value) = self.get() {
return Ok(value);
}
// This is not how the unstable `OnceLock::get_or_try_init` works. That only starts `f` if
// no other `f` is executing and the value is not initialized. However, correctly implementing that is
// hard (one has properly handle panics in `f`) and not doable with the stable API of `OnceLock`.
let value = f()?;
// Another thread might have initialized `self` since we checked that `self.get()` returns `None`. If this is the case, `self.set()`
// returns an error. We ignore it and return the value set by the other
// thread.
let _ = self.set(value);
Ok(self.get().unwrap())
}
fn try_borrow_mut_with<F, E>(&mut self, f: F) -> Result<&mut T, E>
where
F: FnOnce() -> Result<T, E>,
{
let value = if let Some(value) = self.take() {
value
} else {
// This is not how the unstable `OnceLock::get_or_try_init` works. That only starts `f` if
// no other `f` is executing and the value is not initialized. However, correctly implementing that is
// hard (one has properly handle panics in `f`) and not doable with the stable API of `OnceLock`.
f()?
};
// Another thread might have initialized `self` since we checked that `self.get()` returns `None`. If this is the case, `self.set()`
// returns an error. We ignore it and return the value set by the other
// thread.
let _ = self.set(value);
Ok(self.get_mut().unwrap())
}
fn replace(&mut self, new_value: T) -> Option<T> {
if let Some(value) = self.get_mut() {
Some(std::mem::replace(value, new_value))
} else {
let result = self.set(new_value);
assert!(result.is_ok());
None
}
}
fn filled(&self) -> bool {
self.get().is_some()
}
}