You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: text/0000-standard-lazy-types.md
+30-45Lines changed: 30 additions & 45 deletions
Original file line number
Diff line number
Diff line change
@@ -30,9 +30,7 @@ At the same time, working with lazy values in Rust is not easy:
30
30
* C++ and Java provide language-level delayed initialization for static values, while Rust requires explicit code to handle runtime-initialization.
31
31
* Rust borrowing rules require a special pattern when implementing lazy fields.
32
32
33
-
While `lazy_static` is implemented using macros, to work-around language limitations, today it is possible to implement similar functionality without resorting to macros, as a natural combination of two features:
34
-
* lazy values
35
-
*`static` keyword
33
+
`lazy_static` is implemented using macros, to work-around former language limitations. Since then, various language improvements have made it possible to create runtime initialized (lazy) objects in a `static` scope, accomplishing the same goals without macros.
36
34
37
35
We can have a single canonical API for a commonly used tricky unsafe concept, so we probably should have it!
38
36
@@ -104,7 +102,7 @@ Notable features of the API:
104
102
Similarly to other interior mutability primitives, `OnceCell` comes in two flavors:
105
103
106
104
* Non thread-safe `std::cell::OnceCell`.
107
-
* Thread-safe `std::sync::OnceCell`.
105
+
* Thread-safe `std::sync::OnceLock`.
108
106
109
107
Here's how `OnceCell` can be used to implement lazy-initialized global data:
110
108
@@ -142,37 +140,35 @@ impl Ctx {
142
140
}
143
141
```
144
142
145
-
We also provide a more convenient but less powerful `Lazy<T, F>`wrapper around `OnceCell<T>`, which allows to specify the initializing closure at creation time:
143
+
We also provide the more convenient but less powerful `Lazy<T, F>`and `LazyLock<T, F>` wrappers around `OnceCell<T>` and `OnceLock<T>`, which allows specifying the initializing closure at creation time:
146
144
147
145
```rust
148
-
pubstructLazy<T, F=fn() ->T> { ... }
146
+
pubstructLazyCell<T, F=fn() ->T> { ... }
149
147
150
-
impl<T, F> Lazy<T, F> {
148
+
impl<T, F:FnOnce() ->T> LazyCell<T, F> {
151
149
/// Creates a new lazy value with the given initializing function.
152
-
pubconstfnnew(init:F) ->Lazy<T, F>;
153
-
}
154
-
155
-
impl<T, F:FnOnce() ->T> Lazy<T, F> {
150
+
pubconstfnnew(init:F) ->LazyCell<T, F>;
151
+
156
152
/// Forces the evaluation of this lazy value and returns a reference to
157
153
/// the result.
158
154
///
159
155
/// This is equivalent to the `Deref` impl, but is explicit.
However, `#[thread_local]` attribute is pretty far from stabilization at the moment, and due to the required special handling of destructors, it's unclear if just using `cell::Lazy` will work out.
193
188
194
189
Unlike `lazy_static!`, `Lazy` can be used for locals:
195
190
196
191
```rust
197
-
usestd::cell::Lazy;
192
+
usestd::cell::LazyCell;
198
193
199
194
fnmain() {
200
195
letctx=vec![1, 2, 3];
201
-
letthunk=Lazy::new(|| {
196
+
letthunk=LazyCell::new(|| {
202
197
ctx.iter().sum::<i32>()
203
198
});
204
199
assert_eq!(*thunk, 6);
@@ -212,12 +207,12 @@ The proposed API is directly copied from [`once_cell`] crate.
212
207
213
208
Altogether, this RFC proposes to add four types:
214
209
215
-
*`std::cell::OnceCell`, `std::cell::Lazy`
216
-
*`std::sync::OnceCell`, `std::sync::Lazy`
210
+
*`std::cell::OnceCell`, `std::cell::LazyCell`
211
+
*`std::sync::OnceLock`, `std::sync::LazyLock`
217
212
218
-
`OnceCell`is an important core primitive.
219
-
`Lazy`can be stabilized separately from `OnceCell`, or it can be omitted from the standard library altogether.
220
-
However, it provides significantly nicer ergonomics for the common use-case of static lazy values.
213
+
`OnceCell`and `OnceLock` are important primitives.
214
+
`LazyCell ` and `LazyLock`can be stabilized separately from `OnceCell`, or optionally omitted from the standard library altogether.
215
+
However, as they provide significantly nicer ergonomics for the common usecase of static lazy values, it is worth developing in tandem.
221
216
222
217
Non thread-safe flavor is implemented by storing an `UnsafeCell<Option<T>>`:
223
218
@@ -247,27 +242,27 @@ Non thread-safe flavor can be added to `core` as well.
247
242
248
243
The thread-safe variant is implemented similarly to `std::sync::Once`.
249
244
Crucially, it has support for blocking: if many threads call `get_or_init` concurrently, only one will be able to execute the closure, while all other threads will block.
250
-
For this reason, most of `std::sync::OnceCell` API can not be provided in `core`.
245
+
For this reason, most of `std::sync::OnceLock` API can not be provided in `core`.
251
246
In the `sync` case, reliably panicking on re-entrant initialization is not trivial.
252
247
For this reason, the implementation would simply deadlock, with a note that a deadlock might be elevated to a panic in the future.
253
248
254
249
# Drawbacks
255
250
[drawbacks]: #drawbacks
256
251
257
252
* This is a moderately large addition to stdlib, there's a chance we do something wrong.
258
-
This can be mitigated by piece-wise stabilization (in particular, `Lazy` convenience types are optional) and the fact that API is tested in the crates.io ecosystem via `once_cell` crate.
253
+
This can be mitigated by piece-wise stabilization (in particular, `LazyCell` convenience types are optional) and the fact that API is tested in the crates.io ecosystem via `once_cell` crate.
259
254
260
-
* The design of `Lazy` type uses default type-parameter as a work-around for the absence of type-inference of statics.
255
+
* The design of `LazyCell` type uses default type-parameter as a workaround for the absence of typeinference of statics.
261
256
262
257
* We use the same name for unsync and sync types, which might be confusing.
0 commit comments