#
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}