tutorial: More improvements to closure section
This commit is contained in:
parent
3f06a8c8d5
commit
afb3980f29
100
doc/tutorial.md
100
doc/tutorial.md
@ -1012,67 +1012,90 @@ call_twice(bare_function);
|
||||
|
||||
### Do syntax
|
||||
|
||||
Because closures in Rust are frequently used in combination with
|
||||
higher-order functions to simulate control structures like `if` and
|
||||
`loop`. For example, this one iterates over a vector of integers
|
||||
backwards:
|
||||
Closures in Rust are frequently used in combination with higher-order
|
||||
functions to simulate control structures like `if` and
|
||||
`loop`. Consider this function that iterates over a vector of
|
||||
integers, applying an operator to each:
|
||||
|
||||
~~~~
|
||||
fn for_rev(v: ~[int], act: fn(int)) {
|
||||
let mut i = vec::len(v);
|
||||
while (i > 0u) {
|
||||
i -= 1u;
|
||||
act(v[i]);
|
||||
}
|
||||
fn each(v: ~[int], op: fn(int)) {
|
||||
let mut n = 0;
|
||||
while n < v.len() {
|
||||
op(v[n]);
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
~~~~
|
||||
|
||||
To run such an iteration on a block of code, you could call
|
||||
it with a closure containing a block of code.
|
||||
As a caller, if we use a closure to provide the final operator
|
||||
argument, we can write it in a way that has a pleasant, block-like
|
||||
structure.
|
||||
|
||||
~~~~
|
||||
# fn for_rev(v: ~[int], act: fn(int)) {}
|
||||
# fn each(v: ~[int], op: fn(int)) {}
|
||||
# fn do_some_work(i: int) { }
|
||||
for_rev(~[1, 2, 3], |n| {
|
||||
each(~[1, 2, 3], |n| {
|
||||
#debug("%i", n);
|
||||
do_some_work(n);
|
||||
});
|
||||
~~~~
|
||||
|
||||
Because this is such a common pattern Rust has a special form
|
||||
of function call that can be written more like a built-in control
|
||||
structure:
|
||||
This is such a useful pattern that Rust has a special form of function
|
||||
call that can be written more like a built-in control structure:
|
||||
|
||||
~~~~
|
||||
# fn for_rev(v: [int], act: fn(int)) {}
|
||||
# fn each(v: ~[int], op: fn(int)) {}
|
||||
# fn do_some_work(i: int) { }
|
||||
do for_rev(~[1, 2, 3]) |n| {
|
||||
do each(~[1, 2, 3]) |n| {
|
||||
#debug("%i", n);
|
||||
do_some_work(n);
|
||||
}
|
||||
~~~~
|
||||
|
||||
Notice that the call is prefixed with the keyword `do` and, instead of
|
||||
writing the final closure inside the argument list it is moved outside
|
||||
of the parenthesis where it looks visually more like a typical block
|
||||
of code. The `do` expression is purely syntactic sugar for a call
|
||||
that takes a final closure argument.
|
||||
The call is prefixed with the keyword `do` and, instead of writing the
|
||||
final closure inside the argument list it is moved outside of the
|
||||
parenthesis where it looks visually more like a typical block of
|
||||
code. The `do` expression is purely syntactic sugar for a call that
|
||||
takes a final closure argument.
|
||||
|
||||
`do` is often used for task spawning.
|
||||
|
||||
~~~~
|
||||
import task::spawn;
|
||||
|
||||
do spawn() || {
|
||||
#debug("I'm a task, whatever");
|
||||
}
|
||||
~~~~
|
||||
|
||||
That's nice, but look at all those bars and parentheses - that's two empty
|
||||
argument lists back to back. Wouldn't it be great if they weren't
|
||||
there?
|
||||
|
||||
~~~~
|
||||
# import task::spawn;
|
||||
do spawn {
|
||||
#debug("Kablam!");
|
||||
}
|
||||
~~~~
|
||||
|
||||
Empty argument lists can be omitted from `do` expressions.
|
||||
|
||||
### For loops
|
||||
|
||||
`for` loops, like `do` expressions, allow functions to be used as
|
||||
as control structures. `for` loops can be used to treat functions
|
||||
with the proper signature as looping constructs, supporting
|
||||
`break`, `cont` and early returns.
|
||||
Most iteration in Rust is done with `for` loops. Like `do`,
|
||||
`for` is a nice syntax for doing control flow with closures.
|
||||
Additionally, within a `for` loop, `break, `cont`, and `ret`
|
||||
work just as they do with `while` and `loop`.
|
||||
|
||||
Take for example this `each` function that iterates over a vector,
|
||||
breaking early when the iteratee returns `false`:
|
||||
Consider again our `each` function, this time improved to
|
||||
break early when the iteratee returns `false`:
|
||||
|
||||
~~~~
|
||||
fn each<T>(v: &[T], f: fn(T) -> bool) {
|
||||
fn each(v: ~[int], op: fn(int) -> bool) {
|
||||
let mut n = 0;
|
||||
while n < v.len() {
|
||||
if !f(v[n]) {
|
||||
if !op(v[n]) {
|
||||
break;
|
||||
}
|
||||
n += 1;
|
||||
@ -1093,10 +1116,11 @@ each(~[2, 4, 8, 5, 16], |n| {
|
||||
});
|
||||
~~~~
|
||||
|
||||
You can see how that gets noisy. As a syntactic convenience, if the
|
||||
call is preceded by the keyword `for`, the block will implicitly
|
||||
return `true`, and `break` and `cont` can be used, much like in a
|
||||
`while` loop, to explicitly return `false` or `true`.
|
||||
With `for`, functions like `each` can be treated more
|
||||
like builtin looping structures. When calling `each`
|
||||
in a `for` loop, instead of returning `false` to break
|
||||
out of the loop, you just write `break`. To continue
|
||||
to the next iteration, write `cont`.
|
||||
|
||||
~~~~
|
||||
# import each = vec::each;
|
||||
@ -1110,7 +1134,7 @@ for each(~[2, 4, 8, 5, 16]) |n| {
|
||||
~~~~
|
||||
|
||||
As an added bonus, you can use the `ret` keyword, which is not
|
||||
normally allowed in blocks, in a block that appears as the body of a
|
||||
normally allowed in closures, in a block that appears as the body of a
|
||||
`for` loop — this will cause a return to happen from the outer
|
||||
function, not just the loop body.
|
||||
|
||||
@ -1124,6 +1148,8 @@ fn contains(v: ~[int], elt: int) -> bool {
|
||||
}
|
||||
~~~~
|
||||
|
||||
`for` syntax only works with stack closures.
|
||||
|
||||
|
||||
# Datatypes
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user