Skip to content

Commit 52f7a4a

Browse files
committed
auto merge of #19338 : nikomatsakis/rust/unboxed-closure-purge-the-proc, r=acrichto
They are replaced with unboxed closures. cc @pcwalton @aturon This is a [breaking-change]. Mostly, uses of `proc()` simply need to be converted to `move||` (unboxed closures), but in some cases the adaptations required are more complex (particularly for library authors). A detailed write-up can be found here: http://smallcultfollowing.com/babysteps/blog/2014/11/26/purging-proc/ The commits are ordered to emphasize the more important changes, but are not truly standalone.
2 parents 3a9305c + f6d60f3 commit 52f7a4a

File tree

270 files changed

+1259
-1682
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

270 files changed

+1259
-1682
lines changed

src/compiletest/compiletest.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
#![crate_type = "bin"]
12-
#![feature(phase, slicing_syntax, globs)]
12+
#![feature(phase, slicing_syntax, globs, unboxed_closures)]
1313

1414
#![deny(warnings)]
1515

@@ -23,6 +23,7 @@ use std::os;
2323
use std::io;
2424
use std::io::fs;
2525
use std::str::FromStr;
26+
use std::thunk::{Thunk};
2627
use getopts::{optopt, optflag, reqopt};
2728
use common::Config;
2829
use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Codegen};
@@ -369,16 +370,16 @@ pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
369370
let config = (*config).clone();
370371
// FIXME (#9639): This needs to handle non-utf8 paths
371372
let testfile = testfile.as_str().unwrap().to_string();
372-
test::DynTestFn(proc() {
373+
test::DynTestFn(Thunk::new(move || {
373374
runtest::run(config, testfile)
374-
})
375+
}))
375376
}
376377

377378
pub fn make_metrics_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
378379
let config = (*config).clone();
379380
// FIXME (#9639): This needs to handle non-utf8 paths
380381
let testfile = testfile.as_str().unwrap().to_string();
381-
test::DynMetricFn(proc(mm) {
382+
test::DynMetricFn(box move |: mm: &mut test::MetricMap| {
382383
runtest::run_metrics(config, testfile, mm)
383384
})
384385
}

src/compiletest/runtest.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
445445
loop {
446446
//waiting 1 second for gdbserver start
447447
timer::sleep(Duration::milliseconds(1000));
448-
let result = task::try(proc() {
448+
let result = task::try(move || {
449449
tcp::TcpStream::connect("127.0.0.1:5039").unwrap();
450450
});
451451
if result.is_err() {

src/doc/guide-tasks.md

+28-22
Original file line numberDiff line numberDiff line change
@@ -29,31 +29,37 @@ with a closure argument. `spawn` executes the closure in the new task.
2929
fn print_message() { println!("I am running in a different task!"); }
3030
spawn(print_message);
3131
32-
// Alternatively, use a `proc` expression instead of a named function.
33-
// The `proc` expression evaluates to an (unnamed) proc.
34-
// That proc will call `println!(...)` when the spawned task runs.
35-
spawn(proc() println!("I am also running in a different task!") );
32+
// Alternatively, use a `move ||` expression instead of a named function.
33+
// `||` expressions evaluate to an unnamed closure. The `move` keyword
34+
// indicates that the closure should take ownership of any variables it
35+
// touches.
36+
spawn(move || println!("I am also running in a different task!"));
3637
```
3738

3839
In Rust, a task is not a concept that appears in the language semantics.
3940
Instead, Rust's type system provides all the tools necessary to implement safe
4041
concurrency: particularly, ownership. The language leaves the implementation
4142
details to the standard library.
4243

43-
The `spawn` function has a very simple type signature: `fn spawn(f: proc():
44-
Send)`. Because it accepts only procs, and procs contain only owned data,
45-
`spawn` can safely move the entire proc and all its associated state into an
46-
entirely different task for execution. Like any closure, the function passed to
47-
`spawn` may capture an environment that it carries across tasks.
44+
The `spawn` function has the type signature: `fn
45+
spawn<F:FnOnce()+Send>(f: F)`. This indicates that it takes as
46+
argument a closure (of type `F`) that it will run exactly once. This
47+
closure is limited to capturing `Send`-able data from its environment
48+
(that is, data which is deeply owned). Limiting the closure to `Send`
49+
ensures that `spawn` can safely move the entire closure and all its
50+
associated state into an entirely different task for execution.
4851

4952
```{rust}
5053
# use std::task::spawn;
5154
# fn generate_task_number() -> int { 0 }
5255
// Generate some state locally
5356
let child_task_number = generate_task_number();
5457
55-
spawn(proc() {
56-
// Capture it in the remote task
58+
spawn(move || {
59+
// Capture it in the remote task. The `move` keyword indicates
60+
// that this closure should move `child_task_number` into its
61+
// environment, rather than capturing a reference into the
62+
// enclosing stack frame.
5763
println!("I am child number {}", child_task_number);
5864
});
5965
```
@@ -74,7 +80,7 @@ example of calculating two results concurrently:
7480
7581
let (tx, rx): (Sender<int>, Receiver<int>) = channel();
7682
77-
spawn(proc() {
83+
spawn(move || {
7884
let result = some_expensive_computation();
7985
tx.send(result);
8086
});
@@ -102,7 +108,7 @@ task.
102108
# use std::task::spawn;
103109
# fn some_expensive_computation() -> int { 42 }
104110
# let (tx, rx) = channel();
105-
spawn(proc() {
111+
spawn(move || {
106112
let result = some_expensive_computation();
107113
tx.send(result);
108114
});
@@ -135,13 +141,13 @@ results across a number of tasks? The following program is ill-typed:
135141
# fn some_expensive_computation() -> int { 42 }
136142
let (tx, rx) = channel();
137143
138-
spawn(proc() {
144+
spawn(move || {
139145
tx.send(some_expensive_computation());
140146
});
141147
142148
// ERROR! The previous spawn statement already owns the sender,
143149
// so the compiler will not allow it to be captured again
144-
spawn(proc() {
150+
spawn(move || {
145151
tx.send(some_expensive_computation());
146152
});
147153
```
@@ -154,7 +160,7 @@ let (tx, rx) = channel();
154160
for init_val in range(0u, 3) {
155161
// Create a new channel handle to distribute to the child task
156162
let child_tx = tx.clone();
157-
spawn(proc() {
163+
spawn(move || {
158164
child_tx.send(some_expensive_computation(init_val));
159165
});
160166
}
@@ -179,7 +185,7 @@ reference, written with multiple streams, it might look like the example below.
179185
// Create a vector of ports, one for each child task
180186
let rxs = Vec::from_fn(3, |init_val| {
181187
let (tx, rx) = channel();
182-
spawn(proc() {
188+
spawn(move || {
183189
tx.send(some_expensive_computation(init_val));
184190
});
185191
rx
@@ -207,7 +213,7 @@ fn fib(n: u64) -> u64 {
207213
12586269025
208214
}
209215
210-
let mut delayed_fib = Future::spawn(proc() fib(50));
216+
let mut delayed_fib = Future::spawn(move || fib(50));
211217
make_a_sandwich();
212218
println!("fib(50) = {}", delayed_fib.get())
213219
# }
@@ -236,7 +242,7 @@ fn partial_sum(start: uint) -> f64 {
236242
}
237243
238244
fn main() {
239-
let mut futures = Vec::from_fn(200, |ind| Future::spawn( proc() { partial_sum(ind) }));
245+
let mut futures = Vec::from_fn(200, |ind| Future::spawn(move || partial_sum(ind)));
240246
241247
let mut final_res = 0f64;
242248
for ft in futures.iter_mut() {
@@ -278,7 +284,7 @@ fn main() {
278284
for num in range(1u, 10) {
279285
let task_numbers = numbers_arc.clone();
280286
281-
spawn(proc() {
287+
spawn(move || {
282288
println!("{}-norm = {}", num, pnorm(task_numbers.as_slice(), num));
283289
});
284290
}
@@ -312,7 +318,7 @@ if it were local.
312318
# let numbers_arc = Arc::new(numbers);
313319
# let num = 4;
314320
let task_numbers = numbers_arc.clone();
315-
spawn(proc() {
321+
spawn(move || {
316322
// Capture task_numbers and use it as if it was the underlying vector
317323
println!("{}-norm = {}", num, pnorm(task_numbers.as_slice(), num));
318324
});
@@ -344,7 +350,7 @@ result with an `int` field (representing a successful result) or an `Err` result
344350
# use std::task;
345351
# fn some_condition() -> bool { false }
346352
# fn calculate_result() -> int { 0 }
347-
let result: Result<int, Box<std::any::Any + Send>> = task::try(proc() {
353+
let result: Result<int, Box<std::any::Any + Send>> = task::try(move || {
348354
if some_condition() {
349355
calculate_result()
350356
} else {

src/doc/guide.md

+34-51
Original file line numberDiff line numberDiff line change
@@ -4235,36 +4235,16 @@ fn main() {
42354235
}
42364236
```
42374237

4238-
## Procs
4238+
## Moving closures
42394239

4240-
Rust has a second type of closure, called a **proc**. Procs are created
4241-
with the `proc` keyword:
4242-
4243-
```{rust}
4244-
let x = 5i;
4245-
4246-
let p = proc() { x * x };
4247-
println!("{}", p()); // prints 25
4248-
```
4249-
4250-
There is a big difference between procs and closures: procs may only be called once. This
4251-
will error when we try to compile:
4252-
4253-
```{rust,ignore}
4254-
let x = 5i;
4255-
4256-
let p = proc() { x * x };
4257-
println!("{}", p());
4258-
println!("{}", p()); // error: use of moved value `p`
4259-
```
4260-
4261-
This restriction is important. Procs are allowed to consume values that they
4262-
capture, and thus have to be restricted to being called once for soundness
4263-
reasons: any value consumed would be invalid on a second call.
4264-
4265-
Procs are most useful with Rust's concurrency features, and so we'll just leave
4266-
it at this for now. We'll talk about them more in the "Tasks" section of the
4267-
guide.
4240+
Rust has a second type of closure, called a **moving closure**. Moving
4241+
closures are indicated using the `move` keyword (e.g., `move || x *
4242+
x`). The difference between a moving closure and an ordinary closure
4243+
is that a moving closure always takes ownership of all variables that
4244+
it uses. Ordinary closures, in contrast, just create a reference into
4245+
the enclosing stack frame. Moving closures are most useful with Rust's
4246+
concurrency features, and so we'll just leave it at this for
4247+
now. We'll talk about them more in the "Tasks" section of the guide.
42684248

42694249
## Accepting closures as arguments
42704250

@@ -5231,28 +5211,30 @@ concurrency libraries can be written for Rust to help in specific scenarios.
52315211
Here's an example of creating a task:
52325212

52335213
```{rust}
5234-
spawn(proc() {
5214+
spawn(move || {
52355215
println!("Hello from a task!");
52365216
});
52375217
```
52385218

5239-
The `spawn` function takes a proc as an argument, and runs that proc in a new
5240-
task. A proc takes ownership of its entire environment, and so any variables
5241-
that you use inside the proc will not be usable afterward:
5219+
The `spawn` function takes a closure as an argument, and runs that
5220+
closure in a new task. Typically, you will want to use a moving
5221+
closure, so that the closure takes ownership of any variables that it
5222+
touches. This implies that those variables are not usable from the
5223+
parent task after the child task is spawned:
52425224

52435225
```{rust,ignore}
52445226
let mut x = vec![1i, 2i, 3i];
52455227
5246-
spawn(proc() {
5228+
spawn(move || {
52475229
println!("The value of x[0] is: {}", x[0]);
52485230
});
52495231
52505232
println!("The value of x[0] is: {}", x[0]); // error: use of moved value: `x`
52515233
```
52525234

5253-
`x` is now owned by the proc, and so we can't use it anymore. Many other
5254-
languages would let us do this, but it's not safe to do so. Rust's borrow
5255-
checker catches the error.
5235+
`x` is now owned by the closure, and so we can't use it anymore. Many
5236+
other languages would let us do this, but it's not safe to do
5237+
so. Rust's borrow checker catches the error.
52565238

52575239
If tasks were only able to capture these values, they wouldn't be very useful.
52585240
Luckily, tasks can communicate with each other through **channel**s. Channels
@@ -5261,7 +5243,7 @@ work like this:
52615243
```{rust}
52625244
let (tx, rx) = channel();
52635245
5264-
spawn(proc() {
5246+
spawn(move || {
52655247
tx.send("Hello from a task!".to_string());
52665248
});
52675249
@@ -5281,7 +5263,7 @@ If you want to send messages to the task as well, create two channels!
52815263
let (tx1, rx1) = channel();
52825264
let (tx2, rx2) = channel();
52835265
5284-
spawn(proc() {
5266+
spawn(move || {
52855267
tx1.send("Hello from a task!".to_string());
52865268
let message = rx2.recv();
52875269
println!("{}", message);
@@ -5293,8 +5275,9 @@ println!("{}", message);
52935275
tx2.send("Goodbye from main!".to_string());
52945276
```
52955277

5296-
The proc has one sending end and one receiving end, and the main task has one
5297-
of each as well. Now they can talk back and forth in whatever way they wish.
5278+
The closure has one sending end and one receiving end, and the main
5279+
task has one of each as well. Now they can talk back and forth in
5280+
whatever way they wish.
52985281

52995282
Notice as well that because `Sender` and `Receiver` are generic, while you can
53005283
pass any kind of information through the channel, the ends are strongly typed.
@@ -5310,34 +5293,34 @@ a useful thing to use:
53105293
```{rust}
53115294
use std::sync::Future;
53125295
5313-
let mut delayed_value = Future::spawn(proc() {
5296+
let mut delayed_value = Future::spawn(move || {
53145297
// just return anything for examples' sake
53155298
53165299
12345i
53175300
});
53185301
println!("value = {}", delayed_value.get());
53195302
```
53205303

5321-
Calling `Future::spawn` works just like `spawn()`: it takes a proc. In this
5322-
case, though, you don't need to mess with the channel: just have the proc
5323-
return the value.
5304+
Calling `Future::spawn` works just like `spawn()`: it takes a
5305+
closure. In this case, though, you don't need to mess with the
5306+
channel: just have the closure return the value.
53245307

53255308
`Future::spawn` will return a value which we can bind with `let`. It needs
53265309
to be mutable, because once the value is computed, it saves a copy of the
53275310
value, and if it were immutable, it couldn't update itself.
53285311

5329-
The proc will go on processing in the background, and when we need the final
5330-
value, we can call `get()` on it. This will block until the result is done,
5331-
but if it's finished computing in the background, we'll just get the value
5332-
immediately.
5312+
The future will go on processing in the background, and when we need
5313+
the final value, we can call `get()` on it. This will block until the
5314+
result is done, but if it's finished computing in the background,
5315+
we'll just get the value immediately.
53335316

53345317
## Success and failure
53355318

53365319
Tasks don't always succeed, they can also panic. A task that wishes to panic
53375320
can call the `panic!` macro, passing a message:
53385321

53395322
```{rust}
5340-
spawn(proc() {
5323+
spawn(move || {
53415324
panic!("Nope.");
53425325
});
53435326
```
@@ -5349,7 +5332,7 @@ notify other tasks that it has panicked. We can do this with `task::try`:
53495332
use std::task;
53505333
use std::rand;
53515334
5352-
let result = task::try(proc() {
5335+
let result = task::try(move || {
53535336
if rand::random() {
53545337
println!("OK");
53555338
} else {

0 commit comments

Comments
 (0)