@@ -17,8 +17,7 @@ let var = String::from("foo");
17
17
thread :: scope (| s | {
18
18
s . spawn (| _ | println! (" borrowed from thread #1: {}" , var ));
19
19
s . spawn (| _ | println! (" borrowed from thread #2: {}" , var ));
20
- })
21
- . unwrap ();
20
+ });
22
21
```
23
22
24
23
# Motivation
@@ -81,7 +80,7 @@ handle1.join().unwrap();
81
80
handle2 . join (). unwrap ();
82
81
```
83
82
84
- Scoped threads coming to the rescue! By opening a new ` thread::scope() ` block,
83
+ Scoped threads to the rescue! By opening a new ` thread::scope() ` block,
85
84
we can prove to the compiler that all threads spawned within this scope will
86
85
also die inside the scope:
87
86
@@ -99,8 +98,7 @@ thread::scope(|s| {
99
98
100
99
handle1 . join (). unwrap ();
101
100
handle2 . join (). unwrap ();
102
- })
103
- . unwrap ();
101
+ });
104
102
```
105
103
106
104
That means variables living outside the scope can be borrowed without any
@@ -120,13 +118,11 @@ thread::scope(|s| {
120
118
s . spawn (| _ | {
121
119
println! (" thread #2 says: {}" , greeting );
122
120
});
123
- })
124
- . unwrap ();
121
+ });
125
122
```
126
123
127
- Note that ` thread::scope() ` returns a ` Result ` that will be ` Ok ` if all
128
- automatically joined threads have successfully completed, i.e. they haven't
129
- panicked.
124
+ When taking advantage of automatic joining in this way, note that ` thread::scope() `
125
+ will panic if any of the automatically joined threads has panicked.
130
126
131
127
You might've noticed that scoped threads now take a single argument, which is
132
128
just another reference to ` s ` . Since ` s ` lives inside the scope, we cannot borrow
@@ -139,8 +135,7 @@ thread::scope(|s| {
139
135
println! (" I belong to the same `thread::scope()` as my parent thread" )
140
136
});
141
137
});
142
- })
143
- . unwrap ();
138
+ });
144
139
```
145
140
146
141
# Reference-level explanation
@@ -165,7 +160,7 @@ inside the scope. The lifetime relations are:
165
160
Next, we need the ` scope() ` and ` spawn() ` functions:
166
161
167
162
``` rust
168
- fn scope <'env , F , T >(f : F ) -> Result < T >
163
+ fn scope <'env , F , T >(f : F ) -> T
169
164
where
170
165
F : FnOnce (& Scope <'env >) -> T ;
171
166
@@ -202,28 +197,6 @@ impl Builder {
202
197
}
203
198
```
204
199
205
- It's also worth pointing out what exactly happens at the scope end when all
206
- unjoined threads get automatically joined. If all joins succeed, we take
207
- the result of the main closure passed to ` scope() ` and wrap it inside ` Ok ` .
208
-
209
- If any thread panics (and in fact multiple threads can panic), we collect
210
- all those panics into a ` Vec ` , box it, and finally wrap it inside ` Err ` .
211
- The error type is then erased because ` thread::Result<T> ` is just an
212
- alias for:
213
-
214
- ``` rust
215
- Result <T , Box <dyn Any + Send + 'static >>
216
- ```
217
-
218
- This way we can do ` thread::scope(...).unwrap() ` to propagate all panics
219
- in child threads into the main parent thread.
220
-
221
- If the main ` scope() ` closure has panicked after spawning threads, we
222
- just resume unwinding after joining child threads.
223
-
224
- Crossbeam's logic for error handling can be found
225
- [ here] ( https://github.com/crossbeam-rs/crossbeam/blob/79210d6ae34a3e84b23546d8abc5c4b81b206019/crossbeam-utils/src/thread.rs#L167-L193 ) .
226
-
227
200
# Drawbacks
228
201
[ drawbacks ] : #drawbacks
229
202
@@ -276,9 +249,9 @@ Rust 1.0 era. The new one is from the last year's big revamp:
276
249
277
250
There are several differences between old and new scoped threads:
278
251
279
- 1 . ` scope() ` now returns a ` thread::Result<T> ` rather than ` T ` . This is because
280
- panics in the old design were just silently ignored, which is not good .
281
- By returning a ` Result ` , the user can handle panics in whatever way they want .
252
+ 1 . ` scope() ` now propagates unhandled panics from child threads.
253
+ In the old design, panics were silently ignored.
254
+ Users can still handle panics by manually working with ` ScopedJoinHandle ` s .
282
255
283
256
2 . The closure passed to ` Scope::spawn() ` now takes a ` &Scope<'env> ` argument that
284
257
allows one to spawn nested threads, which was not possible with the old design.
@@ -292,14 +265,12 @@ There are several differences between old and new scoped threads:
292
265
293
266
Rayon also has [ scopes] ( https://docs.rs/rayon/1.0.3/rayon/struct.Scope.html ) ,
294
267
but they work on a different abstraction level - Rayon spawns tasks rather than
295
- threads. Its API is almost the same as proposed in this RFC, the only
296
- difference being that ` scope() ` propagates panics instead of returning ` Result ` .
297
- This behavior makes more sense for tasks than threads.
268
+ threads. Its API is the same as the one proposed in this RFC.
298
269
299
270
# Unresolved questions
300
271
[ unresolved-questions ] : #unresolved-questions
301
272
302
- None.
273
+ Can this concept be extended to async? Would there be any behavioral or API differences?
303
274
304
275
# Future possibilities
305
276
[ future-possibilities ] : #future-possibilities
0 commit comments