@@ -41,6 +41,8 @@ Scoped threads in [Crossbeam](https://docs.rs/crossbeam/0.7.1/crossbeam/thread/i
41
41
have matured through years of experience and today we have a design that feels solid
42
42
enough to be promoted into the standard library.
43
43
44
+ See the [ rationale-and-alternatives] ( #rationale-and-alternatives ) section for more.
45
+
44
46
# Guide-level explanation
45
47
[ guide-level-explanation ] : #guide-level-explanation
46
48
@@ -144,63 +146,45 @@ thread::scope(|s| {
144
146
# Reference-level explanation
145
147
[ reference-level-explanation ] : #reference-level-explanation
146
148
147
- We add two new types to the ` std::thread ` module:
149
+ We add a single new type to the ` std::thread ` module:
148
150
149
151
``` rust
150
- struct Scope <'env > {}
151
- struct ScopedJoinHandle <'scope , T > {}
152
- ```
153
-
154
- Lifetime ` 'env ` represents the environment outside the scope, while
155
- ` 'scope ` represents the scope itself. More precisely, everything
156
- outside the scope outlives ` 'env ` and ` 'scope ` outlives everything
157
- inside the scope. The lifetime relations are:
158
-
159
- ```
160
- 'variables_outside: 'env: 'scope: 'variables_inside
152
+ struct Scope <'a > {}
161
153
```
162
154
163
155
Next, we need the ` scope() ` and ` spawn() ` functions:
164
156
165
157
``` rust
166
- fn scope <'env , F , T >(f : F ) -> Result <T >
158
+ fn scope <'a , F , T >(f : F ) -> Result <T >
167
159
where
168
- F : FnOnce (& Scope <'env >) -> T ;
160
+ F : FnOnce (& Scope <'a >) -> T ;
169
161
170
- impl <'env > Scope <'env > {
171
- fn spawn <' scope , F , T >(& ' scope self , f : F ) -> ScopedJoinHandle <' scope , T >
162
+ impl <'a > Scope <'a > {
163
+ fn spawn <F , T >(& self , f : F ) -> JoinHandle < T >
172
164
where
173
- F : FnOnce (& Scope <'env >) -> T + Send + 'env ,
174
- T : Send + 'env ;
165
+ F : FnOnce (& Scope <'a >) -> T + Send + 'a ,
166
+ T : Send + 'a ;
175
167
}
176
168
```
177
169
178
- That's the gist of scoped threads, really.
179
-
180
- Now we just need two more things to make the API complete. First, ` ScopedJoinHandle `
181
- is equivalent to ` JoinHandle ` but tied to the ` 'scope ` lifetime, so it will have
182
- the same methods. Second, the thread builder needs to be able to spawn threads
183
- inside a scope:
170
+ There's just one more thing that will make the API complete: The thread builder
171
+ needs to be able to spawn threads inside a scope.
184
172
185
173
``` rust
186
- impl <'scope , T > ScopedJoinHandle <'scope , T > {
187
- fn join (self ) -> Result <T >;
188
- fn thread (& self ) -> & Thread ;
189
- }
190
-
191
174
impl Builder {
192
- fn spawn_scoped <'scope , 'env , F , T >(
193
- self ,
194
- & 'scope Scope <'env >,
195
- f : F ,
196
- ) -> io :: Result <ScopedJoinHandle <'scope , T >>
175
+ fn spawn_scoped <'a , F , T >(self , scope : & Scope <'a >, f : F ) -> io :: Result <JoinHandle <T >>
197
176
where
198
- F : FnOnce (& Scope <'env >) -> T + Send + 'env ,
199
- T : Send + 'env ;
200
- }
177
+ F : FnOnce (& Scope <'a >) -> T + Send + 'a ,
178
+ T : Send + 'a ;
201
179
```
202
180
203
- It's also worth pointing out what exactly happens at the scope end when all
181
+ Note that this interface is a bit simpler than the one in Crossbeam
182
+ because we can now merge ` JoinHandle ` and ` ScopedJoinHandle ` into a single type.
183
+ Moreover, in Crossbeam, ` ScopedJoinHandle ` is generic over ` 'scope ` , which is
184
+ not really necessary for soundness so we can remove that lifetime to simplify
185
+ things further.
186
+
187
+ It's also worth discussing what exactly happens at the scope end when all
204
188
unjoined threads get automatically joined. If all joins succeed, we take
205
189
the result of the main closure passed to ` scope() ` and wrap it inside ` Ok ` .
206
190
@@ -231,21 +215,35 @@ The main drawback is that scoped threads make the standard library a little bit
231
215
[ rationale-and-alternatives ] : #rationale-and-alternatives
232
216
233
217
The alternative is to keep scoped threads in external crates. However, there are
234
- several advantages to having them in the standard library.
235
-
236
- This is a very common and useful utility and is great for learning, testing, and exploratory
237
- programming. Every person learning Rust will at some point encounter interaction
238
- of borrowing and threads. There's a very important lesson to be taught that threads
239
- * can* in fact borrow local variables, but the standard library doesn't reflect this.
240
-
241
- Some might argue we should discourage using threads altogether and point people to
242
- executors like Rayon and Tokio instead. But still,
243
- the fact that ` thread::spawn() ` requires ` F: 'static ` and there's no way around it
244
- feels like a missing piece in the standard library.
245
-
246
- Finally, it's indisputable that users keep asking for scoped threads on IRC and forums
247
- all the time. Having them as a "blessed" pattern in ` std::thread ` would be beneficial
248
- to everyone.
218
+ several advantages to having them in the standard library:
219
+
220
+ * This is a very common and useful utility and is great for learning, testing, and exploratory
221
+ programming. Every person learning Rust will at some point encounter interaction
222
+ of borrowing and threads. There's a very important lesson to be taught that threads
223
+ * can* in fact borrow local variables, but the standard library doesn't reflect this.
224
+
225
+ * Some might argue we should discourage using threads altogether and point people to
226
+ executors like Rayon and Tokio instead. But still,
227
+ the fact that ` thread::spawn() ` requires ` F: 'static ` and there's no way around it
228
+ feels like a missing piece in the standard library.
229
+
230
+ * Implementing scoped threads is very tricky to get right so it's good to have a
231
+ reliable solution provided by the standard library. Also, scoped threads in ` libstd `
232
+ will be simpler because we don't need to introduce a special type for
233
+ [ scoped join handles] ( https://docs.rs/crossbeam/0.7.1/crossbeam/thread/struct.ScopedJoinHandle.html )
234
+ or [ builders] ( https://docs.rs/crossbeam/0.7.1/crossbeam/thread/struct.ScopedThreadBuilder.html ) .
235
+
236
+ * There are many examples in the official documentation and books that could be
237
+ simplified by scoped threads.
238
+
239
+ * Scoped threads are typically a better default than ` thread::spawn() ` because
240
+ they make sure spawned threads are joined and don't get accidentally "leaked".
241
+ This is sometimes a problem in unit tests, where "dangling" threads can accumulate
242
+ if unit tests spawn threads and forget to join them.
243
+
244
+ * It's indisputable that users keep asking for scoped threads on IRC and forums
245
+ all the time. Having them as a "blessed" pattern in ` std::thread ` would be beneficial
246
+ to everyone.
249
247
250
248
# Prior art
251
249
[ prior-art ] : #prior-art
0 commit comments