Skip to content

Commit d5d0343

Browse files
committed
feat[std]: use Try trait to make OnceLock::get_or_try_init generic over result type
1 parent a6236fa commit d5d0343

File tree

3 files changed

+38
-19
lines changed

3 files changed

+38
-19
lines changed

library/std/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@
272272
#![feature(staged_api)]
273273
#![feature(thread_local)]
274274
#![feature(try_blocks)]
275+
#![feature(try_trait_v2)]
276+
#![feature(try_trait_v2_residual)]
275277
#![feature(utf8_chunks)]
276278
// tidy-alphabetical-end
277279
//

library/std/src/sync/once_lock.rs

Lines changed: 30 additions & 17 deletions
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

@@ -173,8 +174,12 @@ impl<T> OnceLock<T> {
173174
}
174175

175176
/// Gets the contents of the cell, initializing it with `f` if
176-
/// the cell was empty. If the cell was empty and `f` failed, an
177-
/// error is returned.
177+
/// the cell was empty. If the cell was empty and `f` short-circuits,
178+
/// the residual value is returned.
179+
///
180+
/// The return type of this method depends on the return type of the
181+
/// closure. If it returns `Result<T, E>`, the output is `Result<&T, E>`.
182+
/// If it returns `Option<T>`, the output is `Option<&T>`.
178183
///
179184
/// # Panics
180185
///
@@ -194,6 +199,7 @@ impl<T> OnceLock<T> {
194199
///
195200
/// let cell = OnceLock::new();
196201
/// assert_eq!(cell.get_or_try_init(|| Err(())), Err(()));
202+
/// assert_eq!(cell.get_or_try_init(|| None), None);
197203
/// assert!(cell.get().is_none());
198204
/// let value = cell.get_or_try_init(|| -> Result<i32, ()> {
199205
/// Ok(92)
@@ -203,24 +209,30 @@ impl<T> OnceLock<T> {
203209
/// ```
204210
#[inline]
205211
#[unstable(feature = "once_cell_try", issue = "109737")]
206-
pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E>
212+
pub fn get_or_try_init<'a, F, R>(&'a self, f: F) -> <R::Residual as Residual<&'a T>>::TryType
207213
where
208-
F: FnOnce() -> Result<T, E>,
214+
F: FnOnce() -> R,
215+
R: Try<Output = T>,
216+
R::Residual: Residual<&'a T>,
209217
{
210218
// Fast path check
211219
// NOTE: We need to perform an acquire on the state in this method
212220
// in order to correctly synchronize `LazyLock::force`. This is
213221
// currently done by calling `self.get()`, which in turn calls
214222
// `self.is_initialized()`, which in turn performs the acquire.
215223
if let Some(value) = self.get() {
216-
return Ok(value);
224+
return Try::from_output(value);
217225
}
218-
self.initialize(f)?;
219226

220-
debug_assert!(self.is_initialized());
227+
match self.initialize(f) {
228+
ControlFlow::Continue(()) => {
229+
debug_assert!(self.is_initialized());
221230

222-
// SAFETY: The inner value has been initialized
223-
Ok(unsafe { self.get_unchecked() })
231+
// SAFETY: The inner value has been initialized
232+
Try::from_output(unsafe { self.get_unchecked() })
233+
}
234+
ControlFlow::Break(residual) => FromResidual::from_residual(residual),
235+
}
224236
}
225237

226238
/// Consumes the `OnceLock`, returning the wrapped value. Returns
@@ -283,30 +295,31 @@ impl<T> OnceLock<T> {
283295
}
284296

285297
#[cold]
286-
fn initialize<F, E>(&self, f: F) -> Result<(), E>
298+
fn initialize<F, R>(&self, f: F) -> ControlFlow<R::Residual>
287299
where
288-
F: FnOnce() -> Result<T, E>,
300+
F: FnOnce() -> R,
301+
R: Try<Output = T>,
289302
{
290-
let mut res: Result<(), E> = Ok(());
303+
let mut result = ControlFlow::Continue(());
291304
let slot = &self.value;
292305

293306
// Ignore poisoning from other threads
294307
// If another thread panics, then we'll be able to run our closure
295308
self.once.call_once_force(|p| {
296-
match f() {
297-
Ok(value) => {
309+
match f().branch() {
310+
ControlFlow::Continue(value) => {
298311
unsafe { (&mut *slot.get()).write(value) };
299312
}
300-
Err(e) => {
301-
res = Err(e);
313+
ControlFlow::Break(residual) => {
314+
result = ControlFlow::Break(residual);
302315

303316
// Treat the underlying `Once` as poisoned since we
304317
// failed to initialize our value. Calls
305318
p.poison();
306319
}
307320
}
308321
});
309-
res
322+
result
310323
}
311324

312325
/// # Safety

library/std/src/sync/once_lock/tests.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,18 +86,22 @@ fn clone() {
8686

8787
#[test]
8888
fn get_or_try_init() {
89-
let cell: OnceLock<String> = OnceLock::new();
89+
let mut cell: OnceLock<String> = OnceLock::new();
9090
assert!(cell.get().is_none());
9191

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

97+
assert_eq!(cell.get_or_try_init(|| None), None);
9798
assert_eq!(cell.get_or_try_init(|| Err(())), Err(()));
9899

99100
assert_eq!(cell.get_or_try_init(|| Ok::<_, ()>("hello".to_string())), Ok(&"hello".to_string()));
100-
assert_eq!(cell.get(), Some(&"hello".to_string()));
101+
assert_eq!(cell.take(), Some("hello".to_string()));
102+
103+
assert_eq!(cell.get_or_try_init(|| Some("42".to_string())), Some(&"42".to_string()));
104+
assert_eq!(cell.get(), Some(&"42".to_string()));
101105
}
102106

103107
#[test]

0 commit comments

Comments
 (0)