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
//! 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>;
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 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 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()
}
}