Repository navigation
Tracking Issue for once_cell #74465
Copy link
Copy link
Closed
Labels
A-concurrencyArea: ConcurrencyArea: ConcurrencyB-unstableBlocker: Implemented in the nightly compiler and unstable.Blocker: Implemented in the nightly compiler and unstable.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCCategory: An issue tracking the progress of sth. like the implementation of an RFCI-libs-radarLibs issues that are tracked on the team's radar.Libs issues that are tracked on the team's radar.T-libs-api[DEPRECATED; DO NOT USE][DEPRECATED; DO NOT USE]
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
Syncprefix likeSyncLazyfor now, but have a personal preference forAtomiclikeAtomicLazy. 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::synctypes that we might want to just avoid upfront forstd::lazy, especially if that would align with a futurestd::mutexthat doesn't poison. Personally, if we're adding these types tostd::lazyinstead ofstd::sync, I'd be on-board with not worrying about poisoning instd::lazy, and potentially deprecatingstd::sync::Onceandlazy_staticin favour ofstd::lazydown the track if it's possible, rather than attempting to replicate their behavior. cc @Amanieu @sfackler. - Consider making
SyncOnceCell::getblocking. 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
SyncOnceCellin 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]_initthe 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
UnwindSafebounds
Reacted by jyn, Jacob Pratt, Pâris DOUADY, Denis Kolodin, Zac Pullar-Strecker, Taiki Endo, Andrew Banchich, Michael Leonhard, Maximilian Orsley, wyhaya and 111 more
Reacted by Iago-lito, Andrew Banchich, Maximilian Orsley, Eric Shimizu Karbstein, Brooke Holmes, Aleksander Sorokin, Yerkebulan Tulibergenov, Chayim Refael Friedman, A.J. Ruckman, Felipe S. S. Schneider and 16 more
Reacted by Alex Kladov, Sebastian Dröge, Jacob Pratt, Pâris DOUADY, Edward McFarlane, Taiki Endo, Andrew Banchich, Maximilian Orsley, Daniel García, Eric Shimizu Karbstein and 24 more
Metadata
Metadata
Assignees
Labels
A-concurrencyArea: ConcurrencyArea: ConcurrencyB-unstableBlocker: Implemented in the nightly compiler and unstable.Blocker: Implemented in the nightly compiler and unstable.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCCategory: An issue tracking the progress of sth. like the implementation of an RFCI-libs-radarLibs issues that are tracked on the team's radar.Libs issues that are tracked on the team's radar.T-libs-api[DEPRECATED; DO NOT USE][DEPRECATED; DO NOT USE]
Activity
matklad commentedon Jul 24, 2020
Let's cross-out the "should get be blocking?" concern. I decided against this for once_cell, for the following reasons:
get_or_init.matklad commentedon Jul 24, 2020
Added two more open questions from the RFC.
Revert "link once_cell feature to rust-lang#74465"
matklad commentedon Jul 27, 2020
I've added a summary of proposed API to the issue description.
I wonder if makes sense for @rust-lang/libs to do a sort of "API review" here: this is a pretty big chunk of API, and we tried to avoid bike shedding on the RFC.
matklad commentedon Oct 2, 2020
Here's an interesting use-case for non-blocking subset of OnceCell -- building cyclic data structures: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4eceeefc224cdcc719962a9a0e1f72fc
withoutboats commentedon Oct 2, 2020
I strongly expect a method called
getto be nonblocking. I am softly in favor of adding awaitAPI that blocks, but would prefer that it be added in a separate feature later based on demand.matklad commentedon Oct 2, 2020
Yeah, to be clear, there's a consensus that
getshould be non-blocking, the question is resolved. What is not completely solved in my mind, is where we should havecore::lazy::SyncOnceCell. That's possible in theory (by only providinggetandsetmethods), but would be hacky to implement, and of questionable usefulness. The above example is a new use-case for that thing.m-ou-se commentedon Oct 4, 2020
I don't think
Atomicwould be the right word for these. Rust'sAtomictypes and operations (includingArc) never block and never involve the operating system's scheduler (they're all defined incoreoralloc, not std). They're all directly based on the basic atomic operations supported by the processor architecture itself.I'd expect something that's named AtomicLazy/AtomicOnceCell to do the same. And that's something that already exists as another valid strategy for certain Lazy/OnceCell-like types: Instead of blocking all but one thread when multiple threads encounter an 'empty' cell, it wouldn't block but run the initialization function on each of these threads. The first thread to finish atomically stores its initialized value in the cell, and the others simply
drop()the value they created.The
stddoes something similar in a few places (although not wrapped in a type or publicly exposed). For example, here:rust/library/std/src/sys/windows/compat.rs
Lines 65 to 67 in a835b48
And here:
rust/library/std/src/sys/windows/mutex.rs
Lines 119 to 133 in a835b48
And another example in
parking_lot.So, since this Lazy/OnceCell implementation does block (such that the initialization function can be
FnOnceand the type doesn't have to fit in an atomic), and an alternative purely atomic strategy does exist, I'd really avoid using the word 'atomic' in the name here.176 remaining items
github: bump the MSRV to 1.70.0
impl get_mut_or_init and get_mut_or_try_init for OnceCell
impl get_mut_or_init and get_mut_or_try_init for OnceCell
impl get_mut_or_init and get_mut_or_try_init for OnceCell
impl get_mut_or_init and get_mut_or_try_init for OnceCell and OnceLock
Rollup merge of rust-lang#114788 - tisonkun:get_mut_or_init, r=dtolnay
Rollup merge of rust-lang#114788 - tisonkun:get_mut_or_init, r=dtolnay
Rollup merge of rust-lang#114788 - tisonkun:get_mut_or_init, r=dtolnay
Rollup merge of rust-lang#114788 - tisonkun:get_mut_or_init, r=dtolnay
Unrolled build for rust-lang#114788
sdroege commentedon Oct 21, 2024
Are there any plans for making
OnceLock::get_unchecked()(and I guess the mutable version) public? That's currently the only thing preventing me from moving away from theonce_cellcrate.ChayimFriedman2 commentedon Oct 21, 2024
@sdroege You can do `OnceLock::get().unwrap_unchecked().
sdroege commentedon Oct 21, 2024
Thanks, the atomic op and branch in
get()is indeed optimized away when doing that.Rollup merge of #98165 - WaffleLapkin:once_things_renamings, r=m-ou-se
Rollup merge of #98165 - WaffleLapkin:once_things_renamings, r=m-ou-se