cargo/util/once.rs
1//! Extension functions for [`std::sync::OnceLock`] / [`std::cell::OnceCell`]
2//!
3//! This adds polyfills for functionality in `lazycell` that is not stable within `std`.
4
5pub trait OnceExt {
6 type T;
7
8 /// This might run `f` multiple times if different threads start initializing at once.
9 fn try_borrow_with<F, E>(&self, f: F) -> Result<&Self::T, E>
10 where
11 F: FnOnce() -> Result<Self::T, E>;
12
13 fn replace(&mut self, new_value: Self::T) -> Option<Self::T>;
14
15 fn filled(&self) -> bool;
16}
17
18impl<T> OnceExt for std::sync::OnceLock<T> {
19 type T = T;
20
21 fn try_borrow_with<F, E>(&self, f: F) -> Result<&T, E>
22 where
23 F: FnOnce() -> Result<T, E>,
24 {
25 if let Some(value) = self.get() {
26 return Ok(value);
27 }
28
29 // This is not how the unstable `OnceLock::get_or_try_init` works. That only starts `f` if
30 // no other `f` is executing and the value is not initialized. However, correctly implementing that is
31 // hard (one has properly handle panics in `f`) and not doable with the stable API of `OnceLock`.
32 let value = f()?;
33 // Another thread might have initialized `self` since we checked that `self.get()` returns `None`. If this is the case, `self.set()`
34 // returns an error. We ignore it and return the value set by the other
35 // thread.
36 let _ = self.set(value);
37 Ok(self.get().unwrap())
38 }
39
40 fn replace(&mut self, new_value: T) -> Option<T> {
41 if let Some(value) = self.get_mut() {
42 Some(std::mem::replace(value, new_value))
43 } else {
44 let result = self.set(new_value);
45 assert!(result.is_ok());
46 None
47 }
48 }
49
50 fn filled(&self) -> bool {
51 self.get().is_some()
52 }
53}
54
55impl<T> OnceExt for std::cell::OnceCell<T> {
56 type T = T;
57
58 fn try_borrow_with<F, E>(&self, f: F) -> Result<&T, E>
59 where
60 F: FnOnce() -> Result<T, E>,
61 {
62 if let Some(value) = self.get() {
63 return Ok(value);
64 }
65
66 // This is not how the unstable `OnceLock::get_or_try_init` works. That only starts `f` if
67 // no other `f` is executing and the value is not initialized. However, correctly implementing that is
68 // hard (one has properly handle panics in `f`) and not doable with the stable API of `OnceLock`.
69 let value = f()?;
70 // Another thread might have initialized `self` since we checked that `self.get()` returns `None`. If this is the case, `self.set()`
71 // returns an error. We ignore it and return the value set by the other
72 // thread.
73 let _ = self.set(value);
74 Ok(self.get().unwrap())
75 }
76
77 fn replace(&mut self, new_value: T) -> Option<T> {
78 if let Some(value) = self.get_mut() {
79 Some(std::mem::replace(value, new_value))
80 } else {
81 let result = self.set(new_value);
82 assert!(result.is_ok());
83 None
84 }
85 }
86
87 fn filled(&self) -> bool {
88 self.get().is_some()
89 }
90}