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 }})

matklad

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() });

Rendered

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

@matklad matklad added T-libs-api

Relevant to the library API team, which will review and decide on the RFC.

A-types-libstd

Proposals & ideas introducing new types to the standard library.

labels

Oct 18, 2019

Centril

@Centril Centril added A-cell

Proposals relating to interior mutability.

A-sync

Synchronization related proposals & ideas

labels

Oct 18, 2019

@matklad @Centril

Co-Authored-By: Mazdak Farrokhzad twingoow@gmail.com

@pitdicker

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.

@SimonSapin

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! :)

@RustyYato

@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);

@SimonSapin

@KrishnaSannasi How does that help with the requirement that static items name their type without inference?

static FOO: &[_] = &[2, 4];

(Playground)

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

@RustyYato

@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.

@sfackler

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

RalfJung

@pitdicker

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.

@matklad

@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.

F001

F001

@matklad

anp

@matklad

@matklad

@tarcieri

@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.

nixpulvis

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:

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.

@wolfiestyle

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

tgross35

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

@tgross35 @bl-ue

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

Dec 25, 2022

@KnucklesAni

@tgross35

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

@matklad

@matklad

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

@tgross35

You did all the hard work @matklad, I just did the easy part :)

@dbsxdbsx

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)?

@tgross35

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 jxs mentioned this pull request

Apr 19, 2023

3 tasks

@nolik nolik mentioned this pull request

Oct 18, 2023

This was referenced

May 2, 2024

Labels

A-cell

Proposals relating to interior mutability.

A-sync

Synchronization related proposals & ideas

A-types-libstd

Proposals & ideas introducing new types to the standard library.

Libs-Tracked

Libs issues that are tracked on the team's project board.

T-libs-api

Relevant to the library API team, which will review and decide on the RFC.