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