1
Fork 0

tutorial: More improvements to closure section

This commit is contained in:
Brian Anderson 2012-07-04 01:50:51 -07:00
parent 3f06a8c8d5
commit afb3980f29

View file

@ -1012,67 +1012,90 @@ call_twice(bare_function);
### Do syntax ### Do syntax
Because closures in Rust are frequently used in combination with Closures in Rust are frequently used in combination with higher-order
higher-order functions to simulate control structures like `if` and functions to simulate control structures like `if` and
`loop`. For example, this one iterates over a vector of integers `loop`. Consider this function that iterates over a vector of
backwards: integers, applying an operator to each:
~~~~ ~~~~
fn for_rev(v: ~[int], act: fn(int)) { fn each(v: ~[int], op: fn(int)) {
let mut i = vec::len(v); let mut n = 0;
while (i > 0u) { while n < v.len() {
i -= 1u; op(v[n]);
act(v[i]); n += 1;
} }
} }
~~~~ ~~~~
To run such an iteration on a block of code, you could call As a caller, if we use a closure to provide the final operator
it with a closure containing a block of code. 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) { } # fn do_some_work(i: int) { }
for_rev(~[1, 2, 3], |n| { each(~[1, 2, 3], |n| {
#debug("%i", n); #debug("%i", n);
do_some_work(n); do_some_work(n);
}); });
~~~~ ~~~~
Because this is such a common pattern Rust has a special form This is such a useful pattern that Rust has a special form of function
of function call that can be written more like a built-in control call that can be written more like a built-in control structure:
structure:
~~~~ ~~~~
# fn for_rev(v: [int], act: fn(int)) {} # fn each(v: ~[int], op: fn(int)) {}
# fn do_some_work(i: int) { } # fn do_some_work(i: int) { }
do for_rev(~[1, 2, 3]) |n| { do each(~[1, 2, 3]) |n| {
#debug("%i", n); #debug("%i", n);
do_some_work(n); do_some_work(n);
} }
~~~~ ~~~~
Notice that the call is prefixed with the keyword `do` and, instead of The call is prefixed with the keyword `do` and, instead of writing the
writing the final closure inside the argument list it is moved outside final closure inside the argument list it is moved outside of the
of the parenthesis where it looks visually more like a typical block parenthesis where it looks visually more like a typical block of
of code. The `do` expression is purely syntactic sugar for a call code. The `do` expression is purely syntactic sugar for a call that
that takes a final closure argument. 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
`for` loops, like `do` expressions, allow functions to be used as Most iteration in Rust is done with `for` loops. Like `do`,
as control structures. `for` loops can be used to treat functions `for` is a nice syntax for doing control flow with closures.
with the proper signature as looping constructs, supporting Additionally, within a `for` loop, `break, `cont`, and `ret`
`break`, `cont` and early returns. work just as they do with `while` and `loop`.
Take for example this `each` function that iterates over a vector, Consider again our `each` function, this time improved to
breaking early when the iteratee returns `false`: 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; let mut n = 0;
while n < v.len() { while n < v.len() {
if !f(v[n]) { if !op(v[n]) {
break; break;
} }
n += 1; 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 With `for`, functions like `each` can be treated more
call is preceded by the keyword `for`, the block will implicitly like builtin looping structures. When calling `each`
return `true`, and `break` and `cont` can be used, much like in a in a `for` loop, instead of returning `false` to break
`while` loop, to explicitly return `false` or `true`. out of the loop, you just write `break`. To continue
to the next iteration, write `cont`.
~~~~ ~~~~
# import each = vec::each; # 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 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 `for` loop — this will cause a return to happen from the outer
function, not just the loop body. 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 # Datatypes