Closed
Description
This is a tracking issue for the RFC "standard lazy types" (rust-lang/rfcs#2788).
The feature gate for the issue is #![feature(once_cell)]
.
Unstable API
// core::lazy
pub struct OnceCell<T> { .. }
impl<T> OnceCell<T> {
pub const fn new() -> OnceCell<T>;
pub fn get(&self) -> Option<&T>;
pub fn get_mut(&mut self) -> Option<&mut T>;
pub fn set(&self, value: T) -> Result<(), T>;
pub fn get_or_init<F>(&self, f: F) -> &T where F: FnOnce() -> T;
pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E> where F: FnOnce() -> Result<T, E>;
pub fn into_inner(self) -> Option<T>;
pub fn take(&mut self) -> Option<T>;
}
impl<T> From<T> for OnceCell<T>;
impl<T> Default for OnceCell<T>;
impl<T: Clone> Clone for OnceCell<T>;
impl<T: PartialEq> PartialEq for OnceCell<T>;
impl<T: Eq> Eq for OnceCell<T>;
impl<T: fmt::Debug> fmt::Debug for OnceCell<T>;
pub struct Lazy<T, F = fn() -> T> { .. }
impl<T, F> Lazy<T, F> {
pub const fn new(init: F) -> Lazy<T, F>;
}
impl<T, F: FnOnce() -> T> Lazy<T, F> {
pub fn force(this: &Lazy<T, F>) -> &T;
}
impl<T: Default> Default for Lazy<T>;
impl<T, F: FnOnce() -> T> Deref for Lazy<T, F>;
impl<T: fmt::Debug, F> fmt::Debug for Lazy<T, F>;
// std::lazy
pub struct SyncOnceCell<T> { .. }
impl<T> SyncOnceCell<T> {
pub const fn new() -> SyncOnceCell<T>;
pub fn get(&self) -> Option<&T>;
pub fn get_mut(&mut self) -> Option<&mut T>;
pub fn set(&self, value: T) -> Result<(), T>;
pub fn get_or_init<F>(&self, f: F) -> &T where F: FnOnce() -> T;
pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E> where F: FnOnce() -> Result<T, E>;
pub fn into_inner(mut self) -> Option<T>;
pub fn take(&mut self) -> Option<T>;
fn is_initialized(&self) -> bool;
fn initialize<F, E>(&self, f: F) -> Result<(), E> where F: FnOnce() -> Result<T, E>;
unsafe fn get_unchecked(&self) -> &T;
unsafe fn get_unchecked_mut(&mut self) -> &mut T;
}
impl<T> From<T> for SyncOnceCell<T>;
impl<T> Default for SyncOnceCell<T>;
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for SyncOnceCell<T>;
impl<T: UnwindSafe> UnwindSafe for SyncOnceCell<T>;
impl<T: Clone> Clone for SyncOnceCell<T>;
impl<T: PartialEq> PartialEq for SyncOnceCell<T>;
impl<T: Eq> Eq for SyncOnceCell<T>;
unsafe impl<T: Sync + Send> Sync for SyncOnceCell<T>;
unsafe impl<T: Send> Send for SyncOnceCell<T>;
impl<T: fmt::Debug> fmt::Debug for SyncOnceCell<T>;
pub struct SyncLazy<T, F = fn() -> T>;
impl<T, F> SyncLazy<T, F> {
pub const fn new(f: F) -> SyncLazy<T, F>;
}
impl<T, F: FnOnce() -> T> SyncLazy<T, F> {
pub fn force(this: &SyncLazy<T, F>) -> &T;
}
impl<T, F: FnOnce() -> T> Deref for SyncLazy<T, F>;
impl<T: Default> Default for SyncLazy<T>;
impl<T, F: UnwindSafe> RefUnwindSafe for SyncLazy<T, F> where SyncOnceCell<T>: RefUnwindSafe;
impl<T, F: UnwindSafe> UnwindSafe for SyncLazy<T, F> where SyncOnceCell<T>: UnwindSafe;
unsafe impl<T, F: Send> Sync for SyncLazy<T, F> where SyncOnceCell<T>: Sync;
impl<T: fmt::Debug, F> fmt::Debug for SyncLazy<T, F>;
Steps
- Complete the RFC process over at standard lazy types rfcs#2788
- FCP Partial stabilization of
once_cell
#105587 (comment) - Stabilization PR: Partial stabilization of
once_cell
#105587
Unresolved Questions
Inlined from #72414:
- Naming. I'm ok to just roll with the
Sync
prefix likeSyncLazy
for now, but have a personal preference forAtomic
likeAtomicLazy
. Resolved in: Tracking Issue foronce_cell
#74465 (comment). Surprisingly, after more than a year of deliberation we actually found a better name. - Poisoning. It seems like there's some regret around poisoning in other
std::sync
types that we might want to just avoid upfront forstd::lazy
, especially if that would align with a futurestd::mutex
that doesn't poison. Personally, if we're adding these types tostd::lazy
instead ofstd::sync
, I'd be on-board with not worrying about poisoning instd::lazy
, and potentially deprecatingstd::sync::Once
andlazy_static
in favour ofstd::lazy
down the track if it's possible, rather than attempting to replicate their behavior. cc @Amanieu @sfackler. - Consider making
SyncOnceCell::get
blocking. There doesn't seem to be consensus in the linked PR on whether or not that's strictly better than the non-blocking variant. (resolved in Tracking Issue foronce_cell
#74465 (comment)). - Atomic Ordering. the implementation currently use
Release/Acquire
, but it could also use the elusive Consume ordering. Should we spec that we guaranteeRelease/Acquire
? (resolved as yes: consume ordering is not defined enough to merit inclusion into std) - Sync no_std subset. It seems plausible that we might provide some subset of
SyncOnceCell
in no_std. I think there's consensus that we don't want to include "blocking" parts of API, but it's unclear if non-blocking subset (get+set) would be useful. (resolved in Tracking Issue foronce_cell
#74465 (comment)). - Method naming is
get_or[_try]_init
the best name? (resolved as yes in Update once_cell 'get_or_init' to 'get_or_init_with' #107184)
Implementation history
- Add lazy initialization primitives to std #68198 (closed in favor of Add lazy initialization primitives to std #72414)
- Add lazy initialization primitives to std #72414 initial imlementation
- Fix RefUnwindSafe & UnwinsSafe impls for lazy::SyncLazy #74814 fixed
UnwindSafe
bounds