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}