Skip to content

Commit 95e195f

Browse files
committed
impl get_mut_or_init and get_mut_or_try_init for OnceCell and OnceLock
See also #74465 (comment) Signed-off-by: tison <[email protected]>
1 parent 548e14b commit 95e195f

File tree

2 files changed

+165
-9
lines changed

2 files changed

+165
-9
lines changed

library/core/src/cell/once.rs

+84-9
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,42 @@ impl<T> OnceCell<T> {
164164
}
165165
}
166166

167+
/// Gets the mutable reference of the contents of the cell,
168+
/// initializing it with `f` if the cell was empty.
169+
///
170+
/// # Panics
171+
///
172+
/// If `f` panics, the panic is propagated to the caller, and the cell
173+
/// remains uninitialized.
174+
///
175+
/// # Examples
176+
///
177+
/// ```
178+
/// #![feature(once_cell_get_mut)]
179+
///
180+
/// use std::cell::OnceCell;
181+
///
182+
/// let mut cell = OnceCell::new();
183+
/// let value = cell.get_mut_or_init(|| 92);
184+
/// assert_eq!(*value, 92);
185+
///
186+
/// *value += 2;
187+
/// assert_eq!(*value, 94);
188+
///
189+
/// let value = cell.get_mut_or_init(|| unreachable!());
190+
/// assert_eq!(*value, 94);
191+
/// ```
192+
#[inline]
193+
#[unstable(feature = "once_cell_get_mut", issue = "121641")]
194+
pub fn get_mut_or_init<F>(&mut self, f: F) -> &mut T
195+
where
196+
F: FnOnce() -> T,
197+
{
198+
match self.get_mut_or_try_init(|| Ok::<T, !>(f())) {
199+
Ok(val) => val,
200+
}
201+
}
202+
167203
/// Gets the contents of the cell, initializing it with `f` if
168204
/// the cell was empty. If the cell was empty and `f` failed, an
169205
/// error is returned.
@@ -200,16 +236,55 @@ impl<T> OnceCell<T> {
200236
if let Some(val) = self.get() {
201237
return Ok(val);
202238
}
203-
/// Avoid inlining the initialization closure into the common path that fetches
204-
/// the already initialized value
205-
#[cold]
206-
fn outlined_call<F, T, E>(f: F) -> Result<T, E>
207-
where
208-
F: FnOnce() -> Result<T, E>,
209-
{
210-
f()
239+
self.try_init(f)
240+
}
241+
242+
/// Gets the mutable reference of the contents of the cell, initializing
243+
/// it with `f` if the cell was empty. If the cell was empty and `f` failed,
244+
/// an error is returned.
245+
///
246+
/// # Panics
247+
///
248+
/// If `f` panics, the panic is propagated to the caller, and the cell
249+
/// remains uninitialized.
250+
///
251+
/// # Examples
252+
///
253+
/// ```
254+
/// #![feature(once_cell_get_mut)]
255+
///
256+
/// use std::cell::OnceCell;
257+
///
258+
/// let mut cell: OnceCell<u32> = OnceCell::new();
259+
///
260+
/// // Failed initializers do not change the value
261+
/// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err());
262+
/// assert!(cell.get().is_none());
263+
///
264+
/// let value = cell.get_mut_or_try_init(|| "1234".parse());
265+
/// assert_eq!(value, Ok(&mut 1234));
266+
/// *value.unwrap() += 2;
267+
/// assert_eq!(cell.get(), Some(&1236))
268+
/// ```
269+
#[unstable(feature = "once_cell_get_mut", issue = "121641")]
270+
pub fn get_mut_or_try_init<F, E>(&mut self, f: F) -> Result<&mut T, E>
271+
where
272+
F: FnOnce() -> Result<T, E>,
273+
{
274+
if self.get().is_none() {
275+
self.try_init(f)?;
211276
}
212-
let val = outlined_call(f)?;
277+
Ok(self.get_mut().unwrap())
278+
}
279+
280+
// Avoid inlining the initialization closure into the common path that fetches
281+
// the already initialized value
282+
#[cold]
283+
fn try_init<F, E>(&self, f: F) -> Result<&T, E>
284+
where
285+
F: FnOnce() -> Result<T, E>,
286+
{
287+
let val = f()?;
213288
// Note that *some* forms of reentrant initialization might lead to
214289
// UB (see `reentrant_init` test). I believe that just removing this
215290
// `panic`, while keeping `try_insert` would be sound, but it seems

library/std/src/sync/once_lock.rs

+81
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,46 @@ impl<T> OnceLock<T> {
252252
}
253253
}
254254

255+
/// Gets the mutable reference of the contents of the cell, initializing
256+
/// it with `f` if the cell was empty.
257+
///
258+
/// Many threads may call `get_mut_or_init` concurrently with different
259+
/// initializing functions, but it is guaranteed that only one function
260+
/// will be executed.
261+
///
262+
/// # Panics
263+
///
264+
/// If `f` panics, the panic is propagated to the caller, and the cell
265+
/// remains uninitialized.
266+
///
267+
/// # Examples
268+
///
269+
/// ```
270+
/// #![feature(once_cell_get_mut)]
271+
///
272+
/// use std::sync::OnceLock;
273+
///
274+
/// let mut cell = OnceLock::new();
275+
/// let value = cell.get_mut_or_init(|| 92);
276+
/// assert_eq!(*value, 92);
277+
///
278+
/// *value += 2;
279+
/// assert_eq!(*value, 94);
280+
///
281+
/// let value = cell.get_mut_or_init(|| unreachable!());
282+
/// assert_eq!(*value, 94);
283+
/// ```
284+
#[inline]
285+
#[unstable(feature = "once_cell_get_mut", issue = "121641")]
286+
pub fn get_mut_or_init<F>(&mut self, f: F) -> &mut T
287+
where
288+
F: FnOnce() -> T,
289+
{
290+
match self.get_mut_or_try_init(|| Ok::<T, !>(f())) {
291+
Ok(val) => val,
292+
}
293+
}
294+
255295
/// Gets the contents of the cell, initializing it with `f` if
256296
/// the cell was empty. If the cell was empty and `f` failed, an
257297
/// error is returned.
@@ -303,6 +343,47 @@ impl<T> OnceLock<T> {
303343
Ok(unsafe { self.get_unchecked() })
304344
}
305345

346+
/// Gets the mutable reference of the contents of the cell, initializing
347+
/// it with `f` if the cell was empty. If the cell was empty and `f` failed,
348+
/// an error is returned.
349+
///
350+
/// # Panics
351+
///
352+
/// If `f` panics, the panic is propagated to the caller, and
353+
/// the cell remains uninitialized.
354+
///
355+
/// # Examples
356+
///
357+
/// ```
358+
/// #![feature(once_cell_get_mut)]
359+
///
360+
/// use std::sync::OnceLock;
361+
///
362+
/// let mut cell: OnceLock<u32> = OnceLock::new();
363+
///
364+
/// // Failed initializers do not change the value
365+
/// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err());
366+
/// assert!(cell.get().is_none());
367+
///
368+
/// let value = cell.get_mut_or_try_init(|| "1234".parse());
369+
/// assert_eq!(value, Ok(&mut 1234));
370+
/// *value.unwrap() += 2;
371+
/// assert_eq!(cell.get(), Some(&1236))
372+
/// ```
373+
#[inline]
374+
#[unstable(feature = "once_cell_get_mut", issue = "121641")]
375+
pub fn get_mut_or_try_init<F, E>(&mut self, f: F) -> Result<&mut T, E>
376+
where
377+
F: FnOnce() -> Result<T, E>,
378+
{
379+
if self.get().is_none() {
380+
self.initialize(f)?;
381+
}
382+
debug_assert!(self.is_initialized());
383+
// SAFETY: The inner value has been initialized
384+
Ok(unsafe { self.get_unchecked_mut() })
385+
}
386+
306387
/// Consumes the `OnceLock`, returning the wrapped value. Returns
307388
/// `None` if the cell was empty.
308389
///

0 commit comments

Comments
 (0)