Skip to content

Commit

Permalink
impl get_mut_or_init and get_mut_or_try_init for OnceCell
Browse files Browse the repository at this point in the history
See also rust-lang#74465 (comment)

Signed-off-by: tison <[email protected]>
  • Loading branch information
tisonkun committed Feb 14, 2024
1 parent bb89df6 commit f7ac3dd
Showing 1 changed file with 32 additions and 9 deletions.
41 changes: 32 additions & 9 deletions library/core/src/cell/once.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,16 @@ impl<T> OnceCell<T> {
}
}

#[inline]
pub fn get_mut_or_init<F>(&mut self, f: F) -> &mut T
where
F: FnOnce() -> T,
{
match self.get_mut_or_try_init(|| Ok::<T, !>(f())) {
Ok(val) => val,
}
}

/// Gets the contents of the cell, initializing it with `f` if
/// the cell was empty. If the cell was empty and `f` failed, an
/// error is returned.
Expand Down Expand Up @@ -200,16 +210,29 @@ impl<T> OnceCell<T> {
if let Some(val) = self.get() {
return Ok(val);
}
/// Avoid inlining the initialization closure into the common path that fetches
/// the already initialized value
#[cold]
fn outlined_call<F, T, E>(f: F) -> Result<T, E>
where
F: FnOnce() -> Result<T, E>,
{
f()
self.try_init(f)?;
Ok(self.get().unwrap())
}

pub fn get_mut_or_try_init<F, E>(&mut self, f: F) -> Result<&mut T, E>
where
F: FnOnce() -> Result<T, E>,
{
if let Some(val) = self.get() {
return Ok(val);
}
let val = outlined_call(f)?;
self.try_init(f)?;
Ok(self.get_mut().unwrap())
}

// Avoid inlining the initialization closure into the common path that fetches
// the already initialized value
#[cold]
fn try_init<F, E>(&self, f: F) -> Result<(), E>
where
F: FnOnce() -> Result<T, E>,
{
let val = f()?;
// Note that *some* forms of reentrant initialization might lead to
// UB (see `reentrant_init` test). I believe that just removing this
// `panic`, while keeping `try_insert` would be sound, but it seems
Expand Down

0 comments on commit f7ac3dd

Please sign in to comment.