diff options
| author | Niko Matsakis <niko@alum.mit.edu> | 2014-11-26 10:02:46 -0500 |
|---|---|---|
| committer | Niko Matsakis <niko@alum.mit.edu> | 2014-12-14 04:21:57 -0500 |
| commit | 112faabf949749488f0c2fd51098ff2feb1f6e1c (patch) | |
| tree | f643471e4325ad6762c111b7d67a8805050afa23 | |
| parent | 27676d9aa96bd68f8560ade8b9fa61c9298874ea (diff) | |
| download | rust-112faabf949749488f0c2fd51098ff2feb1f6e1c.tar.gz rust-112faabf949749488f0c2fd51098ff2feb1f6e1c.zip | |
Update guide/intro to take into account the removal of `proc`.
cc @steveklabnick
| -rw-r--r-- | src/doc/guide-tasks.md | 4 | ||||
| -rw-r--r-- | src/doc/guide.md | 85 | ||||
| -rw-r--r-- | src/doc/intro.md | 56 | ||||
| -rw-r--r-- | src/doc/reference.md | 53 |
4 files changed, 80 insertions, 118 deletions
diff --git a/src/doc/guide-tasks.md b/src/doc/guide-tasks.md index 1938d3e3878..4adca43be18 100644 --- a/src/doc/guide-tasks.md +++ b/src/doc/guide-tasks.md @@ -30,7 +30,7 @@ fn print_message() { println!("I am running in a different task!"); } spawn(print_message); // Alternatively, use a `move ||` expression instead of a named function. -// `||` expressions evaluate to an unnamed closures. The `move` keyword +// `||` expressions evaluate to an unnamed closure. The `move` keyword // indicates that the closure should take ownership of any variables it // touches. spawn(move || println!("I am also running in a different task!")); @@ -44,7 +44,7 @@ details to the standard library. The `spawn` function has the type signature: `fn spawn<F:FnOnce()+Send>(f: F)`. This indicates that it takes as argument a closure (of type `F`) that it will run exactly once. This -closure is limited to capturing `Send`-able data form its environment +closure is limited to capturing `Send`-able data from its environment (that is, data which is deeply owned). Limiting the closure to `Send` ensures that `spawn` can safely move the entire closure and all its associated state into an entirely different task for execution. diff --git a/src/doc/guide.md b/src/doc/guide.md index 21043cfef14..6e178a2648d 100644 --- a/src/doc/guide.md +++ b/src/doc/guide.md @@ -4235,36 +4235,16 @@ fn main() { } ``` -## Procs +## Moving closures -Rust has a second type of closure, called a **proc**. Procs are created -with the `proc` keyword: - -```{rust} -let x = 5i; - -let p = proc() { x * x }; -println!("{}", p()); // prints 25 -``` - -There is a big difference between procs and closures: procs may only be called once. This -will error when we try to compile: - -```{rust,ignore} -let x = 5i; - -let p = proc() { x * x }; -println!("{}", p()); -println!("{}", p()); // error: use of moved value `p` -``` - -This restriction is important. Procs are allowed to consume values that they -capture, and thus have to be restricted to being called once for soundness -reasons: any value consumed would be invalid on a second call. - -Procs are most useful with Rust's concurrency features, and so we'll just leave -it at this for now. We'll talk about them more in the "Tasks" section of the -guide. +Rust has a second type of closure, called a **moving closure**. Moving +closures are indicated using the `move` keyword (e.g., `move || x * +x`). The difference between a moving closure and an ordinary closure +is that a moving closure always takes ownership of all variables that +it uses. Ordinary closures, in contrast, just create a reference into +the enclosing stack frame. Moving closures are most useful with Rust's +concurrency features, and so we'll just leave it at this for +now. We'll talk about them more in the "Tasks" section of the guide. ## Accepting closures as arguments @@ -5231,28 +5211,30 @@ concurrency libraries can be written for Rust to help in specific scenarios. Here's an example of creating a task: ```{rust} -spawn(proc() { +spawn(move || { println!("Hello from a task!"); }); ``` -The `spawn` function takes a proc as an argument, and runs that proc in a new -task. A proc takes ownership of its entire environment, and so any variables -that you use inside the proc will not be usable afterward: +The `spawn` function takes a closure as an argument, and runs that +closure in a new task. Typically, you will want to use a moving +closure, so that the closure takes ownership of any variables that it +touches. This implies that those variables are not usable from the +parent task after the child task is spawned: ```{rust,ignore} let mut x = vec![1i, 2i, 3i]; -spawn(proc() { +spawn(move || { println!("The value of x[0] is: {}", x[0]); }); println!("The value of x[0] is: {}", x[0]); // error: use of moved value: `x` ``` -`x` is now owned by the proc, and so we can't use it anymore. Many other -languages would let us do this, but it's not safe to do so. Rust's borrow -checker catches the error. +`x` is now owned by the closure, and so we can't use it anymore. Many +other languages would let us do this, but it's not safe to do +so. Rust's borrow checker catches the error. If tasks were only able to capture these values, they wouldn't be very useful. Luckily, tasks can communicate with each other through **channel**s. Channels @@ -5261,7 +5243,7 @@ work like this: ```{rust} let (tx, rx) = channel(); -spawn(proc() { +spawn(move || { tx.send("Hello from a task!".to_string()); }); @@ -5281,7 +5263,7 @@ If you want to send messages to the task as well, create two channels! let (tx1, rx1) = channel(); let (tx2, rx2) = channel(); -spawn(proc() { +spawn(move || { tx1.send("Hello from a task!".to_string()); let message = rx2.recv(); println!("{}", message); @@ -5293,8 +5275,9 @@ println!("{}", message); tx2.send("Goodbye from main!".to_string()); ``` -The proc has one sending end and one receiving end, and the main task has one -of each as well. Now they can talk back and forth in whatever way they wish. +The closure has one sending end and one receiving end, and the main +task has one of each as well. Now they can talk back and forth in +whatever way they wish. Notice as well that because `Sender` and `Receiver` are generic, while you can pass any kind of information through the channel, the ends are strongly typed. @@ -5310,7 +5293,7 @@ a useful thing to use: ```{rust} use std::sync::Future; -let mut delayed_value = Future::spawn(proc() { +let mut delayed_value = Future::spawn(move || { // just return anything for examples' sake 12345i @@ -5318,18 +5301,18 @@ let mut delayed_value = Future::spawn(proc() { println!("value = {}", delayed_value.get()); ``` -Calling `Future::spawn` works just like `spawn()`: it takes a proc. In this -case, though, you don't need to mess with the channel: just have the proc -return the value. +Calling `Future::spawn` works just like `spawn()`: it takes a +closure. In this case, though, you don't need to mess with the +channel: just have the closure return the value. `Future::spawn` will return a value which we can bind with `let`. It needs to be mutable, because once the value is computed, it saves a copy of the value, and if it were immutable, it couldn't update itself. -The proc will go on processing in the background, and when we need the final -value, we can call `get()` on it. This will block until the result is done, -but if it's finished computing in the background, we'll just get the value -immediately. +The future will go on processing in the background, and when we need +the final value, we can call `get()` on it. This will block until the +result is done, but if it's finished computing in the background, +we'll just get the value immediately. ## Success and failure @@ -5337,7 +5320,7 @@ Tasks don't always succeed, they can also panic. A task that wishes to panic can call the `panic!` macro, passing a message: ```{rust} -spawn(proc() { +spawn(move || { panic!("Nope."); }); ``` @@ -5349,7 +5332,7 @@ notify other tasks that it has panicked. We can do this with `task::try`: use std::task; use std::rand; -let result = task::try(proc() { +let result = task::try(move || { if rand::random() { println!("OK"); } else { diff --git a/src/doc/intro.md b/src/doc/intro.md index e2cccef5b4a..c0a1d5fa881 100644 --- a/src/doc/intro.md +++ b/src/doc/intro.md @@ -391,26 +391,29 @@ Here's an example of a concurrent Rust program: ```{rust} fn main() { for _ in range(0u, 10u) { - spawn(proc() { + spawn(move || { println!("Hello, world!"); }); } } ``` -This program creates ten threads, who all print `Hello, world!`. The `spawn` -function takes one argument, a `proc`. 'proc' is short for 'procedure,' and is -a form of closure. This closure is executed in a new thread, created by `spawn` -itself. - -One common form of problem in concurrent programs is a 'data race.' This occurs -when two different threads attempt to access the same location in memory in a -non-synchronized way, where at least one of them is a write. If one thread is -attempting to read, and one thread is attempting to write, you cannot be sure -that your data will not be corrupted. Note the first half of that requirement: -two threads that attempt to access the same location in memory. Rust's -ownership model can track which pointers own which memory locations, which -solves this problem. +This program creates ten threads, who all print `Hello, world!`. The +`spawn` function takes one argument, a closure, indicated by the +double bars `||`. (The `move` keyword indicates that the closure takes +ownership of any data it uses; we'll have more on the significance of +this shortly.) This closure is executed in a new thread created by +`spawn`. + +One common form of problem in concurrent programs is a 'data race.' +This occurs when two different threads attempt to access the same +location in memory in a non-synchronized way, where at least one of +them is a write. If one thread is attempting to read, and one thread +is attempting to write, you cannot be sure that your data will not be +corrupted. Note the first half of that requirement: two threads that +attempt to access the same location in memory. Rust's ownership model +can track which pointers own which memory locations, which solves this +problem. Let's see an example. This Rust code will not compile: @@ -419,7 +422,7 @@ fn main() { let mut numbers = vec![1i, 2i, 3i]; for i in range(0u, 3u) { - spawn(proc() { + spawn(move || { for j in range(0, 3) { numbers[j] += 1 } }); } @@ -432,8 +435,8 @@ It gives us this error: 6:71 error: capture of moved value: `numbers` for j in range(0, 3) { numbers[j] += 1 } ^~~~~~~ -7:50 note: `numbers` moved into closure environment here because it has type `proc():Send`, which is non-copyable (perhaps you meant to use clone()?) - spawn(proc() { +7:50 note: `numbers` moved into closure environment here + spawn(move || { for j in range(0, 3) { numbers[j] += 1 } }); 6:79 error: cannot assign to immutable dereference (dereference is implicit, due to indexing) @@ -441,11 +444,16 @@ It gives us this error: ^~~~~~~~~~~~~~~ ``` -It mentions that "numbers moved into closure environment". Because we referred -to `numbers` inside of our `proc`, and we create three `proc`s, we would have -three references. Rust detects this and gives us the error: we claim that -`numbers` has ownership, but our code tries to make three owners. This may -cause a safety problem, so Rust disallows it. +It mentions that "numbers moved into closure environment". Because we +declared the closure as a moving closure, and it referred to +`numbers`, the closure will try to take ownership of the vector. But +the closure itself is created in a loop, and hence we will actually +create three closures, one for every iteration of the loop. This means +that all three of those closures would try to own `numbers`, which is +impossible -- `numbers` must have just one owner. Rust detects this +and gives us the error: we claim that `numbers` has ownership, but our +code tries to make three owners. This may cause a safety problem, so +Rust disallows it. What to do here? Rust has two types that helps us: `Arc<T>` and `Mutex<T>`. "Arc" stands for "atomically reference counted." In other words, an Arc will @@ -468,7 +476,7 @@ fn main() { for i in range(0u, 3u) { let number = numbers.clone(); - spawn(proc() { + spawn(move || { let mut array = number.lock(); (*array)[i] += 1; @@ -528,7 +536,7 @@ fn main() { let vec = vec![1i, 2, 3]; for i in range(1u, 3) { - spawn(proc() { + spawn(move || { println!("{}", vec[i]); }); } diff --git a/src/doc/reference.md b/src/doc/reference.md index ee70981d9b2..c24cd6d8bf3 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -187,19 +187,18 @@ grammar as double-quoted strings. Other tokens have exact rules given. <p id="keyword-table-marker"></p> -| | | | | | -|----------|----------|----------|----------|--------| -| abstract | alignof | as | be | box | -| break | const | continue | crate | do | -| else | enum | extern | false | final | -| fn | for | if | impl | in | -| let | loop | match | mod | move | -| mut | offsetof | once | override | priv | -| proc | pub | pure | ref | return | -| sizeof | static | self | struct | super | -| true | trait | type | typeof | unsafe | -| unsized | use | virtual | where | while | -| yield | | | | | +| | | | | | +|----------|----------|----------|----------|---------| +| abstract | alignof | as | be | box | +| break | const | continue | crate | do | +| else | enum | extern | false | final | +| fn | for | if | impl | in | +| let | loop | match | mod | move | +| mut | offsetof | once | override | priv | +| pub | pure | ref | return | sizeof | +| static | self | struct | super | true | +| trait | type | typeof | unsafe | unsized | +| use | virtual | where | while | yield | Each of these keywords has special meaning in its grammar, and all of them are @@ -3842,8 +3841,6 @@ x = bo(5,7); ```{.ebnf .notation} closure_type := [ 'unsafe' ] [ '<' lifetime-list '>' ] '|' arg-list '|' [ ':' bound-list ] [ '->' type ] -procedure_type := 'proc' [ '<' lifetime-list '>' ] '(' arg-list ')' - [ ':' bound-list ] [ '->' type ] lifetime-list := lifetime | lifetime ',' lifetime-list arg-list := ident ':' type | ident ':' type ',' arg-list bound-list := bound | bound '+' bound-list @@ -3852,8 +3849,6 @@ bound := path | lifetime The type of a closure mapping an input of type `A` to an output of type `B` is `|A| -> B`. A closure with no arguments or return values has type `||`. -Similarly, a procedure mapping `A` to `B` is `proc(A) -> B` and a no-argument -and no-return value closure has type `proc()`. An example of creating and calling a closure: @@ -3876,30 +3871,6 @@ call_closure(closure_no_args, closure_args); ``` -Unlike closures, procedures may only be invoked once, but own their -environment, and are allowed to move out of their environment. Procedures are -allocated on the heap (unlike closures). An example of creating and calling a -procedure: - -```rust -let string = "Hello".to_string(); - -// Creates a new procedure, passing it to the `spawn` function. -spawn(proc() { - println!("{} world!", string); -}); - -// the variable `string` has been moved into the previous procedure, so it is -// no longer usable. - - -// Create an invoke a procedure. Note that the procedure is *moved* when -// invoked, so it cannot be invoked again. -let f = proc(n: int) { n + 22 }; -println!("answer: {}", f(20)); - -``` - ### Object types Every trait item (see [traits](#traits)) defines a type with the same name as |
