Skip to content

Commit afb3980

Browse files
committed
tutorial: More improvements to closure section
1 parent 3f06a8c commit afb3980

File tree

1 file changed

+63
-37
lines changed

1 file changed

+63
-37
lines changed

doc/tutorial.md

Lines changed: 63 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,67 +1012,90 @@ call_twice(bare_function);
10121012
10131013
### Do syntax
10141014
1015-
Because closures in Rust are frequently used in combination with
1016-
higher-order functions to simulate control structures like `if` and
1017-
`loop`. For example, this one iterates over a vector of integers
1018-
backwards:
1015+
Closures in Rust are frequently used in combination with higher-order
1016+
functions to simulate control structures like `if` and
1017+
`loop`. Consider this function that iterates over a vector of
1018+
integers, applying an operator to each:
10191019
10201020
~~~~
1021-
fn for_rev(v: ~[int], act: fn(int)) {
1022-
let mut i = vec::len(v);
1023-
while (i > 0u) {
1024-
i -= 1u;
1025-
act(v[i]);
1026-
}
1021+
fn each(v: ~[int], op: fn(int)) {
1022+
let mut n = 0;
1023+
while n < v.len() {
1024+
op(v[n]);
1025+
n += 1;
1026+
}
10271027
}
10281028
~~~~
10291029
1030-
To run such an iteration on a block of code, you could call
1031-
it with a closure containing a block of code.
1030+
As a caller, if we use a closure to provide the final operator
1031+
argument, we can write it in a way that has a pleasant, block-like
1032+
structure.
10321033
10331034
~~~~
1034-
# fn for_rev(v: ~[int], act: fn(int)) {}
1035+
# fn each(v: ~[int], op: fn(int)) {}
10351036
# fn do_some_work(i: int) { }
1036-
for_rev(~[1, 2, 3], |n| {
1037+
each(~[1, 2, 3], |n| {
10371038
#debug("%i", n);
10381039
do_some_work(n);
10391040
});
10401041
~~~~
10411042
1042-
Because this is such a common pattern Rust has a special form
1043-
of function call that can be written more like a built-in control
1044-
structure:
1043+
This is such a useful pattern that Rust has a special form of function
1044+
call that can be written more like a built-in control structure:
10451045
10461046
~~~~
1047-
# fn for_rev(v: [int], act: fn(int)) {}
1047+
# fn each(v: ~[int], op: fn(int)) {}
10481048
# fn do_some_work(i: int) { }
1049-
do for_rev(~[1, 2, 3]) |n| {
1049+
do each(~[1, 2, 3]) |n| {
10501050
#debug("%i", n);
10511051
do_some_work(n);
10521052
}
10531053
~~~~
10541054
1055-
Notice that the call is prefixed with the keyword `do` and, instead of
1056-
writing the final closure inside the argument list it is moved outside
1057-
of the parenthesis where it looks visually more like a typical block
1058-
of code. The `do` expression is purely syntactic sugar for a call
1059-
that takes a final closure argument.
1055+
The call is prefixed with the keyword `do` and, instead of writing the
1056+
final closure inside the argument list it is moved outside of the
1057+
parenthesis where it looks visually more like a typical block of
1058+
code. The `do` expression is purely syntactic sugar for a call that
1059+
takes a final closure argument.
1060+
1061+
`do` is often used for task spawning.
1062+
1063+
~~~~
1064+
import task::spawn;
1065+
1066+
do spawn() || {
1067+
#debug("I'm a task, whatever");
1068+
}
1069+
~~~~
1070+
1071+
That's nice, but look at all those bars and parentheses - that's two empty
1072+
argument lists back to back. Wouldn't it be great if they weren't
1073+
there?
1074+
1075+
~~~~
1076+
# import task::spawn;
1077+
do spawn {
1078+
#debug("Kablam!");
1079+
}
1080+
~~~~
1081+
1082+
Empty argument lists can be omitted from `do` expressions.
10601083
10611084
### For loops
10621085
1063-
`for` loops, like `do` expressions, allow functions to be used as
1064-
as control structures. `for` loops can be used to treat functions
1065-
with the proper signature as looping constructs, supporting
1066-
`break`, `cont` and early returns.
1086+
Most iteration in Rust is done with `for` loops. Like `do`,
1087+
`for` is a nice syntax for doing control flow with closures.
1088+
Additionally, within a `for` loop, `break, `cont`, and `ret`
1089+
work just as they do with `while` and `loop`.
10671090
1068-
Take for example this `each` function that iterates over a vector,
1069-
breaking early when the iteratee returns `false`:
1091+
Consider again our `each` function, this time improved to
1092+
break early when the iteratee returns `false`:
10701093
10711094
~~~~
1072-
fn each<T>(v: &[T], f: fn(T) -> bool) {
1095+
fn each(v: ~[int], op: fn(int) -> bool) {
10731096
let mut n = 0;
10741097
while n < v.len() {
1075-
if !f(v[n]) {
1098+
if !op(v[n]) {
10761099
break;
10771100
}
10781101
n += 1;
@@ -1093,10 +1116,11 @@ each(~[2, 4, 8, 5, 16], |n| {
10931116
});
10941117
~~~~
10951118
1096-
You can see how that gets noisy. As a syntactic convenience, if the
1097-
call is preceded by the keyword `for`, the block will implicitly
1098-
return `true`, and `break` and `cont` can be used, much like in a
1099-
`while` loop, to explicitly return `false` or `true`.
1119+
With `for`, functions like `each` can be treated more
1120+
like builtin looping structures. When calling `each`
1121+
in a `for` loop, instead of returning `false` to break
1122+
out of the loop, you just write `break`. To continue
1123+
to the next iteration, write `cont`.
11001124
11011125
~~~~
11021126
# import each = vec::each;
@@ -1110,7 +1134,7 @@ for each(~[2, 4, 8, 5, 16]) |n| {
11101134
~~~~
11111135
11121136
As an added bonus, you can use the `ret` keyword, which is not
1113-
normally allowed in blocks, in a block that appears as the body of a
1137+
normally allowed in closures, in a block that appears as the body of a
11141138
`for` loop — this will cause a return to happen from the outer
11151139
function, not just the loop body.
11161140
@@ -1124,6 +1148,8 @@ fn contains(v: ~[int], elt: int) -> bool {
11241148
}
11251149
~~~~
11261150
1151+
`for` syntax only works with stack closures.
1152+
11271153
11281154
# Datatypes
11291155

0 commit comments

Comments
 (0)