Skip to content

Commit 9b1c7b9

Browse files
committed
std: use Try trait to make OnceLock::get_or_try_init generic over result type
1 parent 290e0c6 commit 9b1c7b9

File tree

3 files changed

+39
-27
lines changed

3 files changed

+39
-27
lines changed

Diff for: library/std/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,8 @@
361361
#![feature(str_internals)]
362362
#![feature(strict_provenance)]
363363
#![feature(strict_provenance_atomic_ptr)]
364+
#![feature(try_trait_v2)]
365+
#![feature(try_trait_v2_residual)]
364366
#![feature(ub_checks)]
365367
// tidy-alphabetical-end
366368
//

Diff for: library/std/src/sync/once_lock.rs

+31-25
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::cell::UnsafeCell;
22
use crate::fmt;
33
use crate::marker::PhantomData;
44
use crate::mem::MaybeUninit;
5+
use crate::ops::{ControlFlow, FromResidual, Residual, Try};
56
use crate::panic::{RefUnwindSafe, UnwindSafe};
67
use crate::sync::Once;
78

@@ -375,24 +376,23 @@ impl<T> OnceLock<T> {
375376
/// ```
376377
#[inline]
377378
#[unstable(feature = "once_cell_try", issue = "109737")]
378-
pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E>
379+
pub fn get_or_try_init<'a, F, R>(&'a self, f: F) -> <R::Residual as Residual<&'a T>>::TryType
379380
where
380-
F: FnOnce() -> Result<T, E>,
381+
F: FnOnce() -> R,
382+
R: Try<Output = T, Residual: Residual<&'a T>>,
381383
{
382384
// Fast path check
383385
// NOTE: We need to perform an acquire on the state in this method
384386
// in order to correctly synchronize `LazyLock::force`. This is
385-
// currently done by calling `self.get()`, which in turn calls
386-
// `self.is_initialized()`, which in turn performs the acquire.
387-
if let Some(value) = self.get() {
388-
return Ok(value);
387+
// currently done by calling `self.is_initialized()`.
388+
if !self.is_initialized() {
389+
if let ControlFlow::Break(residual) = self.initialize(f) {
390+
return FromResidual::from_residual(residual);
391+
}
389392
}
390-
self.initialize(f)?;
391-
392-
debug_assert!(self.is_initialized());
393393

394394
// SAFETY: The inner value has been initialized
395-
Ok(unsafe { self.get_unchecked() })
395+
try { unsafe { self.get_unchecked() } }
396396
}
397397

398398
/// Gets the mutable reference of the contents of the cell, initializing
@@ -426,16 +426,22 @@ impl<T> OnceLock<T> {
426426
/// ```
427427
#[inline]
428428
#[unstable(feature = "once_cell_get_mut", issue = "121641")]
429-
pub fn get_mut_or_try_init<F, E>(&mut self, f: F) -> Result<&mut T, E>
429+
pub fn get_mut_or_try_init<'a, F, R>(
430+
&'a mut self,
431+
f: F,
432+
) -> <R::Residual as Residual<&'a mut T>>::TryType
430433
where
431-
F: FnOnce() -> Result<T, E>,
434+
F: FnOnce() -> R,
435+
R: Try<Output = T, Residual: Residual<&'a mut T>>,
432436
{
433-
if self.get().is_none() {
434-
self.initialize(f)?;
437+
if !self.is_initialized() {
438+
if let ControlFlow::Break(residual) = self.initialize(f) {
439+
return FromResidual::from_residual(residual);
440+
}
435441
}
436-
debug_assert!(self.is_initialized());
442+
437443
// SAFETY: The inner value has been initialized
438-
Ok(unsafe { self.get_unchecked_mut() })
444+
try { unsafe { self.get_unchecked_mut() } }
439445
}
440446

441447
/// Consumes the `OnceLock`, returning the wrapped value. Returns
@@ -499,22 +505,22 @@ impl<T> OnceLock<T> {
499505

500506
#[cold]
501507
#[optimize(size)]
502-
fn initialize<F, E>(&self, f: F) -> Result<(), E>
508+
fn initialize<F, R>(&self, f: F) -> ControlFlow<R::Residual, ()>
503509
where
504-
F: FnOnce() -> Result<T, E>,
510+
F: FnOnce() -> R,
511+
R: Try<Output = T>,
505512
{
506-
let mut res: Result<(), E> = Ok(());
507-
let slot = &self.value;
513+
let mut res = ControlFlow::Continue(());
508514

509515
// Ignore poisoning from other threads
510516
// If another thread panics, then we'll be able to run our closure
511517
self.once.call_once_force(|p| {
512-
match f() {
513-
Ok(value) => {
514-
unsafe { (&mut *slot.get()).write(value) };
518+
match f().branch() {
519+
ControlFlow::Continue(value) => {
520+
unsafe { (&mut *self.value.get()).write(value) };
515521
}
516-
Err(e) => {
517-
res = Err(e);
522+
ControlFlow::Break(residual) => {
523+
res = ControlFlow::Break(residual);
518524

519525
// Treat the underlying `Once` as poisoned since we
520526
// failed to initialize our value.

Diff for: library/std/src/sync/once_lock/tests.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -83,18 +83,22 @@ fn clone() {
8383
#[test]
8484
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
8585
fn get_or_try_init() {
86-
let cell: OnceLock<String> = OnceLock::new();
86+
let mut cell: OnceLock<String> = OnceLock::new();
8787
assert!(cell.get().is_none());
8888

8989
let res = panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() }));
9090
assert!(res.is_err());
9191
assert!(!cell.is_initialized());
9292
assert!(cell.get().is_none());
9393

94+
assert_eq!(cell.get_or_try_init(|| None), None);
9495
assert_eq!(cell.get_or_try_init(|| Err(())), Err(()));
9596

9697
assert_eq!(cell.get_or_try_init(|| Ok::<_, ()>("hello".to_string())), Ok(&"hello".to_string()));
97-
assert_eq!(cell.get(), Some(&"hello".to_string()));
98+
assert_eq!(cell.take(), Some("hello".to_string()));
99+
100+
assert_eq!(cell.get_or_try_init(|| Some("42".to_string())), Some(&"42".to_string()));
101+
assert_eq!(cell.get(), Some(&"42".to_string()));
98102
}
99103

100104
#[test]

0 commit comments

Comments
 (0)