Skip to content

Explain differences between {Once,Lazy}{Cell,Lock} types - #125696

Merged
bors merged 4 commits into
rust-lang:masterfrom
workingjubilee:please-dont-say-you-are-lazy
Jun 4, 2024
Merged

Explain differences between {Once,Lazy}{Cell,Lock} types#125696
bors merged 4 commits into
rust-lang:masterfrom
workingjubilee:please-dont-say-you-are-lazy

Conversation

@workingjubilee

@workingjubilee workingjubilee commented May 29, 2024

Copy link
Copy Markdown
Member

The question of "which once-ish cell-ish type should I use?" has been raised multiple times, and is especially important now that we have stabilized the LazyCell and LazyLock types. The answer for the Lazy* types is that you would be better off using them if you want to use what is by far the most common pattern: initialize it with a single nullary function that you would call at every get_or_init site. For everything else there's the Once* types.

"For everything else" is a somewhat weak motivation, as it only describes by negation. While contrasting them is inevitable, I feel positive motivations are more understandable. For this, I now offer a distinct example that helps explain why OnceLock can be useful, despite LazyLock existing: you can do some cool stuff with it that LazyLock simply can't support due to its mere definition.

The pair of std::sync::*Locks are usable inside a static, and can serve roles in async or multithreaded (or asynchronously multithreaded) programs that *Cells cannot. Because of this, they received most of my attention.

Fixes #124696
Fixes #125615

@rustbot

rustbot commented May 29, 2024

Copy link
Copy Markdown
Collaborator

r? @Nilstrieb

rustbot has assigned @Nilstrieb.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels May 29, 2024
@workingjubilee

Copy link
Copy Markdown
Member Author

Technically each of these commits can be landed individually but I feel that it's important to discuss the, well, big picture, when trying to answer "so what's the big picture?", so I'm happy to split this up but that's why I made them as a single PR.

@rust-log-analyzer

This comment has been minimized.

@workingjubilee

Copy link
Copy Markdown
Member Author

what, I thought we stabilized those?

@workingjubilee

Copy link
Copy Markdown
Member Author

we did...? #37854 (comment)

@workingjubilee
workingjubilee force-pushed the please-dont-say-you-are-lazy branch from a621faf to f8ddb6d Compare May 29, 2024 06:39
@Kobzol

Kobzol commented May 29, 2024

Copy link
Copy Markdown
Member

These tests are executed with the beta compiler (python3 ../x.py test --stage 0 core alloc std test proc_macro), the stabilization hasn't reached it yet, probably.

@workingjubilee

Copy link
Copy Markdown
Member Author

ah, I see.

@kaffarell kaffarell left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments for the text part of the PR. Did not yet look at the example.

Comment thread library/core/src/cell.rs Outdated
Comment thread library/core/src/cell.rs Outdated
/// In many simple cases, you can use [`LazyLock<T, F>`] instead to get the benefits of this type
/// with less effort: `LazyLock<T, F>` "looks like" `&T` because it initializes with `F` on deref!
/// Where OnceLock shines is when LazyLock is too simple to support a given case, as LazyLock
/// doesn't allow additional inputs to its function after you call [`LazyLock::new(|| ...)`].

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds—at least for me—a bit misleading. I would write something like: "doesn't allow different initialization functions after you call ...". What do you think?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kaffarell The function is nullary.

@kaffarell kaffarell May 31, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, OnceLock (the get_or_init fn) is also nullary, isn't it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any function with N arguments can be curried to a nullary function, but this is not experienced in a profoundly different way than "calling the function". Yes, it is technically a "different function" because you enclose the state, but most people would describe this:

let clos = || f(a, b, c, d, etc);
let value = ONCE_LOCK.get_or_init(clos);

as calling f, instead of calling clos, because they actually write this:

let value = ONCE_LOCK.get_or_init(|| f(a, b, c, d, etc));

Comment thread library/core/src/cell.rs Outdated
Comment thread library/std/src/sync/once_lock.rs Outdated
/// You can use `OnceLock` to implement a type that requires "append-only" logic:
///
/// ```
/// #![feature(once_cell_try_insert)]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you write this without the unstable feature? I don't think it's great to have an example using unstable features

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, as it turns out!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're crazy

Comment thread library/std/src/sync/lazy_lock.rs Outdated
Comment thread library/std/src/sync/lazy_lock.rs Outdated
@workingjubilee
workingjubilee force-pushed the please-dont-say-you-are-lazy branch 2 times, most recently from 4d8ec9f to 9ed7cfc Compare June 3, 2024 05:53
This example is spiritually an example of LazyLock, as it computes a
variable at runtime but accepts no inputs into that process.
It is also slightly simpler and thus easier to understand.
Change it to an even-more concise version and move it to LazyLock.

The example now editorializes slightly more. This may be unnecessary,
but it can be educational for the reader.
While slightly verbose, it helps explain "why bother with OnceLock?"
This is a point of confusion that has been raised multiple times
shortly before and after the stabilization of LazyLock.
@Noratrieb

Copy link
Copy Markdown
Member

@bors r+

@bors

bors commented Jun 4, 2024

Copy link
Copy Markdown
Collaborator

📌 Commit 9ed7cfc has been approved by Nilstrieb

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 4, 2024
bors added a commit to rust-lang-ci/rust that referenced this pull request Jun 4, 2024
…llaumeGomez

Rollup of 11 pull requests

Successful merges:

 - rust-lang#106186 (Add function `core::iter::chain`)
 - rust-lang#125596 (Convert `proc_macro_back_compat` lint to an unconditional error.)
 - rust-lang#125696 (Explain differences between `{Once,Lazy}{Cell,Lock}` types)
 - rust-lang#125917 (Create `run-make` `env_var` and `env_var_os` helpers)
 - rust-lang#125927 (Ignore `vec_deque_alloc_error::test_shrink_to_unwind` test on non-unwind targets)
 - rust-lang#125930 (feat(opt-dist): new flag `--benchmark-cargo-config`)
 - rust-lang#125932 (Fix typo in the docs of `HashMap::raw_entry_mut`)
 - rust-lang#125933 (Update books)
 - rust-lang#125944 (Update fuchsia maintainers)
 - rust-lang#125946 (Include trailing commas in wrapped function declarations [RustDoc])
 - rust-lang#125973 (Remove `tests/run-make-fulldeps/pretty-expanded`)

Failed merges:

 - rust-lang#125815 (`rustc_parse` top-level cleanups)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors
bors merged commit ee04e0f into rust-lang:master Jun 4, 2024
@rustbot rustbot added this to the 1.80.0 milestone Jun 4, 2024
@workingjubilee
workingjubilee deleted the please-dont-say-you-are-lazy branch June 5, 2024 10:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LazyCell, LazyLock are missing some big-picture documentation Explain the difference between Once(Lock|Cell) and Lazy(Lock|Cell) in the docs

8 participants