Skip to content

Commit c5b8d89

Browse files
committed
auto merge of #16309 : steveklabnik/rust/guide_tasks, r=brson
I wasn't 100% sure of what level of detail I wanted to go into things here. For example, 1:1 vs M:N tasks seems like a better distinction to leave to the Guide.
2 parents 0ba2d04 + b1435ed commit c5b8d89

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed

src/doc/guide.md

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4318,6 +4318,152 @@ the same function, so our binary is a little bit larger.
43184318

43194319
# Tasks
43204320

4321+
Concurrency and parallelism are topics that are of increasing interest to a
4322+
broad subsection of software developers. Modern computers are often multi-core,
4323+
to the point that even embedded devices like cell phones have more than one
4324+
processor. Rust's semantics lend themselves very nicely to solving a number of
4325+
issues that programmers have with concurrency. Many concurrency errors that are
4326+
runtime errors in other languages are compile-time errors in Rust.
4327+
4328+
Rust's concurrency primitive is called a **task**. Tasks are lightweight, and
4329+
do not share memory in an unsafe manner, preferring message passing to
4330+
communicate. It's worth noting that tasks are implemented as a library, and
4331+
not part of the language. This means that in the future, other concurrency
4332+
libraries can be written for Rust to help in specific scenarios. Here's an
4333+
example of creating a task:
4334+
4335+
```{rust}
4336+
spawn(proc() {
4337+
println!("Hello from a task!");
4338+
});
4339+
```
4340+
4341+
The `spawn` function takes a proc as an argument, and runs that proc in a new
4342+
task. A proc takes ownership of its entire environment, and so any variables
4343+
that you use inside the proc will not be usable afterward:
4344+
4345+
```{rust,ignore}
4346+
let mut x = vec![1i, 2i, 3i];
4347+
4348+
spawn(proc() {
4349+
println!("The value of x[0] is: {}", x[0]);
4350+
});
4351+
4352+
println!("The value of x[0] is: {}", x[0]); // error: use of moved value: `x`
4353+
```
4354+
4355+
`x` is now owned by the proc, and so we can't use it anymore. Many other
4356+
languages would let us do this, but it's not safe to do so. Rust's type system
4357+
catches the error.
4358+
4359+
If tasks were only able to capture these values, they wouldn't be very useful.
4360+
Luckily, tasks can communicate with each other through **channel**s. Channels
4361+
work like this:
4362+
4363+
```{rust}
4364+
let (tx, rx) = channel();
4365+
4366+
spawn(proc() {
4367+
tx.send("Hello from a task!".to_string());
4368+
});
4369+
4370+
let message = rx.recv();
4371+
println!("{}", message);
4372+
```
4373+
4374+
The `channel()` function returns two endpoints: a `Receiver<T>` and a
4375+
`Sender<T>`. You can use the `.send()` method on the `Sender<T>` end, and
4376+
receive the message on the `Receiver<T>` side with the `recv()` method. This
4377+
method blocks until it gets a message. There's a similar method, `.try_recv()`,
4378+
which returns an `Option<T>` and does not block.
4379+
4380+
If you want to send messages to the task as well, create two channels!
4381+
4382+
```{rust}
4383+
let (tx1, rx1) = channel();
4384+
let (tx2, rx2) = channel();
4385+
4386+
spawn(proc() {
4387+
tx1.send("Hello from a task!".to_string());
4388+
let message = rx2.recv();
4389+
println!("{}", message);
4390+
});
4391+
4392+
let message = rx1.recv();
4393+
println!("{}", message);
4394+
4395+
tx2.send("Goodbye from main!".to_string());
4396+
```
4397+
4398+
The proc has one sending end and one receiving end, and the main task has one
4399+
of each as well. Now they can talk back and forth in whatever way they wish.
4400+
4401+
Notice as well that because `Sender` and `Receiver` are generic, while you can
4402+
pass any kind of information through the channel, the ends are strongly typed.
4403+
If you try to pass a string, and then an integer, Rust will complain.
4404+
4405+
## Futures
4406+
4407+
With these basic primitives, many different concurrency patterns can be
4408+
developed. Rust includes some of these types in its standard library. For
4409+
example, if you wish to compute some value in the background, `Future` is
4410+
a useful thing to use:
4411+
4412+
```{rust}
4413+
use std::sync::Future;
4414+
4415+
let mut delayed_value = Future::spawn(proc() {
4416+
// just return anything for examples' sake
4417+
4418+
12345i
4419+
});
4420+
println!("value = {}", delayed_value.get());
4421+
```
4422+
4423+
Calling `Future::spawn` works just like `spawn()`: it takes a proc. In this
4424+
case, though, you don't need to mess with the channel: just have the proc
4425+
return the value.
4426+
4427+
`Future::spawn` will return a value which we can bind with `let`. It needs
4428+
to be mutable, because once the value is computed, it saves a copy of the
4429+
value, and if it were immutable, it couldn't update itself.
4430+
4431+
The proc will go on processing in the background, and when we need the final
4432+
value, we can call `get()` on it. This will block until the result is done,
4433+
but if it's finished computing in the background, we'll just get the value
4434+
immediately.
4435+
4436+
## Success and failure
4437+
4438+
Tasks don't always succeed, they can also fail. A task that wishes to fail
4439+
can call the `fail!` macro, passing a message:
4440+
4441+
```{rust}
4442+
spawn(proc() {
4443+
fail!("Nope.");
4444+
});
4445+
```
4446+
4447+
If a task fails, it is not possible for it to recover. However, it can
4448+
notify other tasks that it has failed. We can do this with `task::try`:
4449+
4450+
```{rust}
4451+
use std::task;
4452+
use std::rand;
4453+
4454+
let result = task::try(proc() {
4455+
if rand::random() {
4456+
println!("OK");
4457+
} else {
4458+
fail!("oops!");
4459+
}
4460+
});
4461+
```
4462+
4463+
This task will randomly fail or succeed. `task::try` returns a `Result`
4464+
type, so we can handle the response like any other computation that may
4465+
fail.
4466+
43214467
# Macros
43224468

43234469
# Unsafe

0 commit comments

Comments
 (0)