From f7ac3dd30d19c5394e1b72ad4af4a2abc415c3fa Mon Sep 17 00:00:00 2001 From: tison Date: Mon, 14 Aug 2023 09:04:59 +0800 Subject: [PATCH] impl get_mut_or_init and get_mut_or_try_init for OnceCell See also https://github.com/rust-lang/rust/issues/74465#issuecomment-1676522051 Signed-off-by: tison --- library/core/src/cell/once.rs | 41 +++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/library/core/src/cell/once.rs b/library/core/src/cell/once.rs index 3877a0c48cb23..cf2b10e020fb7 100644 --- a/library/core/src/cell/once.rs +++ b/library/core/src/cell/once.rs @@ -164,6 +164,16 @@ impl OnceCell { } } + #[inline] + pub fn get_mut_or_init(&mut self, f: F) -> &mut T + where + F: FnOnce() -> T, + { + match self.get_mut_or_try_init(|| Ok::(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. @@ -200,16 +210,29 @@ impl OnceCell { 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: F) -> Result - where - F: FnOnce() -> Result, - { - f() + self.try_init(f)?; + Ok(self.get().unwrap()) + } + + pub fn get_mut_or_try_init(&mut self, f: F) -> Result<&mut T, E> + where + F: FnOnce() -> Result, + { + 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(&self, f: F) -> Result<(), E> + where + F: FnOnce() -> Result, + { + 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