standard lazy types by matklad · Pull Request #2788 · rust-lang/rfcs (original) (raw)
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Conversation137 Commits15 Checks0 Files changed
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
[ Show hidden characters]({{ revealButtonHref }})
Add support for lazy initialized values to standard library, effectively superseding the popular lazy_static
crate.
use std::sync::Lazy;
// BACKTRACE
implements Deref<Target = Option<String>>
// and is initialized on the first access
static BACKTRACE: Lazy<Option> = Lazy::new(|| {
std::env::var("RUST_BACKTRACE").ok()
});
declanvk, pitdicker, SimonSapin, RustyYato, tarcieri, pcpthm, Pauan, VictorKoenders, kgv, F001, and 291 more reacted with thumbs up emoji bl-ue, TennyZhuang, frederikhors, zohnannor, 8573, and bczhc reacted with laugh emoji tarcieri, anp, iago-lito, krircc, mark-i-m, ibkevg, bstrie, forestsia, yoshuawuyts, leoyvens, and 37 more reacted with hooray emoji tarcieri, , DrSensor, anp, Lythenas, nixpulvis, clarfonthey, krircc, jdm, Nashenas88, and 66 more reacted with heart emoji camsteffen, GrayJack, JarvisCraft, dbstratta, bl-ue, schneiderfelipe, DjDeveloperr, TennyZhuang, lmtr0, frederikhors, and 2 more reacted with rocket emoji bl-ue, odboy, frederikhors, and zohnannor reacted with eyes emoji
matklad added T-libs-api
Relevant to the library API team, which will review and decide on the RFC.
Proposals & ideas introducing new types to the standard library.
labels
Proposals relating to interior mutability.
Synchronization related proposals & ideas
labels
Co-Authored-By: Mazdak Farrokhzad twingoow@gmail.com
Great RFC, I hope it makes it into the standard library!
One other aspect that the crate conquer_once tackles is a distinction between blocking and non-blocking methods. I believe this is one possible way to divide the api?
/// Never blocks, returns `None` if uninitialized, or while another thread is initializing the `OnceCell` concurrently.
pub fn get(&self) -> Option<&T>;
/// Never blocks, returns `Err` if initialized, or while another thread is initializing the `OnceCell` concurrently.
pub fn set(&self, value: T) -> Result<(), T>;
/// Blocks if another thread is initializing the `OnceCell` concurrently.
pub fn get_or_init<F>(&self, f: F) -> &T
where
F: FnOnce() -> T,
;
/// Blocks if another thread is initializing the `OnceCell` concurrently.
pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E>
where
F: FnOnce() -> Result<T, E>,
;
}
Alternatively, all four methods could be blocking, and a try_get
and try_set
may suffice for a non-blocking use case.
But don't let this comment derail the discussion about whether to include OnceCell
in the standard library to much.
fn pointers are not ZSTs, so we waste one pointer per static lazy value. Lazy locals will generally rely on type-inference and will use more specific closure type.
It looks like rust-lang/rust#63065 will allow using a zero-size closure type in a static:
static FOO: Lazy<Foo, impl FnOnce() -> Foo> = Lazy::new(|| foo());
But this will require spelling the item type twice. Maybe that repetition could be avoided… with a macro… that could be named lazy_static!
:)
@SimonSapin or we can add bounds to Lazy
like so
struct Lazy<F: FnOnce<()>> { .. }
and use it like this,
static FOO: Lazy<impl FnOnce() -> Foo> = Lazy(foo);
@KrishnaSannasi How does that help with the requirement that static
items name their type without inference?
static FOO: &[_] = &[2, 4];
Compiling playground v0.0.1 (/playground)
error[E0121]: the type placeholder _
is not allowed within types on item signatures
--> src/lib.rs:1:15
|
1 | static FOO: &[_] = &[2, 4];
| ^ not allowed in type signatures
@SimonSapin I'm not sure what you are asking, I didn't use _
anywhere in my example. It would be nice if we could have type inference in static
/const
that only depended on their declaration, but we don't have that (yet).
You proposed using impl Trait
to get rid of the cost of fn() -> ...
, and noted a paper cut where you would have to name a type multiple times, and I proposed a way to get rid of that paper cut by changing Lazy
by removing the redundant type parameter.
It doesn't really seem all that worth it to me to spend time worrying about how to inline a function that is called at most one time in the entire execution of a program.
RustyYato, pcpthm, kennytm, demurgos, tanriol, withoutboats, luser, JustAPerson, vbfox, AxlLind, and 15 more reacted with thumbs up emoji
A small difference that may be worth noting: std::sync::Once
will only run its closure once, while OnceCell::get_or{_try}_init
will one run its closure once successfully.
@pitdicker pointed out that we actually can have some part of the sync
API exposed via core
. This unfortunately increases design/decision space a bit :) I've amended the rfc.
@jhpratt the author of this RFC @matklad is the author of once_cell
. It also mentions:
The proposed API is directly copied from
once_cell
crate.
Altogether, this RFC proposes to add four types: |
---|
* `std::cell::OnceCell`, `std::cell::Lazy` |
* `std::sync::OnceCell`, `std::sync::Lazy` |
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that we have sync::Once and cell::Cell, but this may be confusing. Just a thought.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is kinda like the combination of both, so the naming may be good (modulo Send
/Sync
concerns). But it would be better if the !Sync
version had a different name from the Sync
version.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps acknowledging OnceCell
as a WORM (write once, read many) might make WormCell
be a better name? Assuming that the implementation isn't too sluggish.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also would really like to see there be a different name for these, rather than relying on the fact that they lie in two separate modules. Maybe just prefixing the sync implementations with Sync
is enough, i.e. SyncLazy
and Lazy
.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would be against WORM terminology, as it seems rather obscure: I don't think I see it often enough to not consult wikipedia. A more appropriate term would probably be SingleAssignmentCell
, but that's a mouthful.
Ultimately, I doubt that there's a perfect solution for cell/sync naming, so the libs team would have to just decide on some particular naming scheme. I am personally ok with Sync
prefix. The disadvantage of SyncX
scheme is that it makes more common case longer to spell, and is a tautology (sync::SyncOnceCell
).
I personally like the proposed cell::OnceCell / sync::OnceCell
variant the most:
- if you want extra clarity, you can qualify the type with
cell::
orsync::
on the call site, instead of importing it - due to how awesome Rust is, it's impossible to use
cell::OnceCell
instead ofsync::OnceCell
, and the mistake in other direction is not really a mistake: just a very slight perf loss - in practice, I think it would be rare for a module to use
cell
andsync
versions simultaneously.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I like the way it's organized in the once_cell
crate with explicit sync::*
and unsync::*
modules.
The part that feels the most strange to me about this currently proposal is that a OnceCell
lives in both cell
(makes sense, since it's a type of cell), and sync
(makes sense, given that Once
is also defined here). Something's conflated here, though this could well be outside the scope of this RFC. Either way, this is the first "cell" added to the sync
module.
For another point of reference, we currently have Rc
defined in std::rc
and a similar Arc
defined in std::sync
. I might argue this would make more sense as std::rc::{Rc, sync::Arc}
, which would solve my issue with OnceCell
as well, though I don't expect this to be possible with backwards compatibility requirements.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, another possibility is to introduce something like lazy and lazy::sync modules. As it stands, std::sync today feels a bit like, well, kitchen sync, it has a bit of everything.
I can imagine an alternative world, where std::sync is only for synchronization primitives, atomic and mpsc are top-level modules and Arc lives in rc. In this world, having an std::lazy module would definitely be better.
With current stdlib structure, std::sync::OnceCell blends better with the background.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, everything in std::cell
is !Sync
so it sort of maps to crates.io’s once_cell::unsync
.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The libs team has discussed moving stuff out of std::sync into more appropriate modules (e.g. std::mutex::Mutex). I think ideally we wouldn't throw more stuff in there.
I was expecting this to implement haskell-style lazy values. Can I use this outside global scope? Maybe use another more descriptive name? like LazyStatic
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some changes from Once
/Lazy
to LazyCell
/OnceCell
/LazyLock
/OnceLock
, updated atomic ordering conclusion section
- Update LazyCell/LazyLock/OnceCell/OnceLock usage
- Clarify stance on atomics
- Some minor flow updates
Co-authored-by: bl-ue 54780737+bl-ue@users.noreply.github.com
KnucklesAni added a commit to yace-project/yace that referenced this pull request
Now that rust-lang/rust#105587 is nearing the end of this FCP, could this be either merged or closed?
I would vouch for merging even if it isn't needed for any direct action. I see RFCs as a good way to document the intended use case - kind of nice to be able to look back at to see "what was the goal with this addition". But I also have no insight or sway into the RFC process so just do as is needed 🙂
This was referenced
Mar 15, 2023
As this is already stable on master
(rust-lang/rust#105587), I've updated links and will merge the RFC, it seems to be sufficiently thoroughly accepted and does not merit an extra FCP and proccess.
Special thanks to @tgross35 for pushing this over the finish line, Rust needs more heroes like you!
tarcieri, andrewbanchich, slanterns, kartva, ChriFo, tgross35, zohnannor, sksat, CathalMullan, lo48576, and 2 more reacted with hooray emoji danielrh, jyn514, and zohnannor reacted with heart emoji
You did all the hard work @matklad, I just did the easy part :)
As this is already stable on
master
(rust-lang/rust#105587), I've updated links and will merge the RFC, it seems to be sufficiently thoroughly accepted and does not merit an extra FCP and proccess.Special thanks to @tgross35 for pushing this over the finish line, Rust needs more heroes like you!
Nice to hear this. Does that mean users could take the new official version to replace lazy_static
and once_cell
in the next version(1.69)?
Nice to hear this. Does that mean users could take the new official version to replace lazy_static and once_cell in the next version(1.69)?
OnceCell
and OnceLock
will be stable in 1.70, and does replace once_cell::{sync, unsync}::OnceCell
(exception of the try
functions, see #109737). LazyCell
and LazyLock
, which are the direct replacements for lazy_static
, are not yet going stable: see rust-lang/rust#109736
edit: LazyCell
and LazyLock
will be stable in 1.80 🎉
This was referenced
Apr 11, 2023
jxs mentioned this pull request
3 tasks
nolik mentioned this pull request
This was referenced
May 2, 2024
Labels
Proposals relating to interior mutability.
Synchronization related proposals & ideas
Proposals & ideas introducing new types to the standard library.
Libs issues that are tracked on the team's project board.
Relevant to the library API team, which will review and decide on the RFC.