Skip to content

Commit 1304e4e

Browse files
committed
update tutorial to avoid block() syntax and to use term stack closure
1 parent b7811e6 commit 1304e4e

File tree

1 file changed

+41
-37
lines changed

1 file changed

+41
-37
lines changed

doc/tutorial.md

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ more detail later on (the `T`s here stand for any other type):
381381
`{field1: T1, field2: T2}`
382382
: Record type.
383383

384-
`fn(arg1: T1, arg2: T2) -> T3`, `lambda()`, `block()`
384+
`fn(arg1: T1, arg2: T2) -> T3`, `fn@()`, `fn~()`, `fn&()`
385385
: Function types.
386386

387387
`@T`, `~T`, `*T`
@@ -824,32 +824,36 @@ functions that can access variables in the scope in which they are
824824
created.
825825

826826
There are several forms of closures, each with its own role. The most
827-
common type is called a 'block', this is a closure which has full
828-
access to its environment.
827+
common type is called a 'stack closure'; this is a closure which has
828+
full access to its environment.
829829

830830
~~~~
831-
fn call_block_with_ten(b: block(int)) { b(10); }
831+
fn call_closure_with_ten(b: fn(int)) { b(10); }
832832
833833
let x = 20;
834-
call_block_with_ten({|arg|
834+
call_closure_with_ten({|arg|
835835
#info("x=%d, arg=%d", x, arg);
836836
});
837837
~~~~
838838

839-
This defines a function that accepts a block, and then calls it with a
840-
simple block that executes a log statement, accessing both its
841-
argument and the variable `x` from its environment.
839+
This defines a function that accepts a closure, and then calls it with
840+
a simple stack closure that executes a log statement, accessing both
841+
its argument and the variable `x` from its environment.
842842

843-
Blocks can only be used in a restricted way, because it is not allowed
844-
to survive the scope in which it was created. They are allowed to
845-
appear in function argument position and in call position, but nowhere
846-
else.
843+
Stack closures are called stack closures because they directly access
844+
the stack frame in which they are created. This makes them very
845+
lightweight to construct and lets them modify local variables from the
846+
enclosing scope, but it also makes it unsafe for the closure to
847+
survive the scope in which it was created. To prevent them from being
848+
used after the creating scope has returned, stack closures can only be
849+
used in a restricted way: they are allowed to appear in function
850+
argument position and in call position, but nowhere else.
847851

848852
### Boxed closures
849853

850-
When you need to store a closure in a data structure, a block will not
851-
do, since the compiler will refuse to let you store it. For this
852-
purpose, Rust provides a type of closure that has an arbitrary
854+
When you need to store a closure in a data structure, a stack closure
855+
will not do, since the compiler will refuse to let you store it. For
856+
this purpose, Rust provides a type of closure that has an arbitrary
853857
lifetime, written `fn@` (boxed closure, analogous to the `@` pointer
854858
type described in the next section).
855859

@@ -879,14 +883,14 @@ fn main() {
879883

880884
A nice property of Rust closures is that you can pass any kind of
881885
closure (as long as the arguments and return types match) to functions
882-
that expect a `block`. Thus, when writing a higher-order function that
886+
that expect a `fn()`. Thus, when writing a higher-order function that
883887
wants to do nothing with its function argument beyond calling it, you
884-
should almost always specify the type of that argument as `block`, so
888+
should almost always specify the type of that argument as `fn()`, so
885889
that callers have the flexibility to pass whatever they want.
886890

887891
~~~~
888-
fn call_twice(f: block()) { f(); f(); }
889-
call_twice({|| "I am a block"; });
892+
fn call_twice(f: fn()) { f(); f(); }
893+
call_twice({|| "I am a stack closure; });
890894
call_twice(fn@() { "I am a boxed closure"; });
891895
fn bare_function() { "I am a plain function"; }
892896
call_twice(bare_function);
@@ -903,9 +907,9 @@ them. Unique closures mostly exist to for spawning new
903907

904908
### Shorthand syntax
905909

906-
The compact syntax used for blocks (`{|arg1, arg2| body}`) can also
907-
be used to express boxed and unique closures in situations where the
908-
closure style can be unambiguously derived from the context. Most
910+
The compact syntax used for stack closures (`{|arg1, arg2| body}`) can
911+
also be used to express boxed and unique closures in situations where
912+
the closure style can be unambiguously derived from the context. Most
909913
notably, when calling a higher-order function you do not have to use
910914
the long-hand syntax for the function you're passing, since the
911915
compiler can look at the argument type to find out what the parameter
@@ -941,12 +945,12 @@ returning the day of the week that string corresponds to (if any).
941945

942946
## Iteration
943947

944-
Functions taking blocks provide a good way to define non-trivial
948+
Functions taking closures provide a good way to define non-trivial
945949
iteration constructs. For example, this one iterates over a vector
946950
of integers backwards:
947951

948952
~~~~
949-
fn for_rev(v: [int], act: block(int)) {
953+
fn for_rev(v: [int], act: fn(int)) {
950954
let i = vec::len(v);
951955
while (i > 0u) {
952956
i -= 1u;
@@ -958,7 +962,7 @@ fn for_rev(v: [int], act: block(int)) {
958962
To run such an iteration, you could do this:
959963

960964
~~~~
961-
# fn for_rev(v: [int], act: block(int)) {}
965+
# fn for_rev(v: [int], act: fn(int)) {}
962966
for_rev([1, 2, 3], {|n| log(error, n); });
963967
~~~~
964968

@@ -967,7 +971,7 @@ moved outside of the parentheses permits the following, which
967971
looks quite like a normal loop:
968972

969973
~~~~
970-
# fn for_rev(v: [int], act: block(int)) {}
974+
# fn for_rev(v: [int], act: fn(int)) {}
971975
for_rev([1, 2, 3]) {|n|
972976
log(error, n);
973977
}
@@ -1387,7 +1391,7 @@ Here we know for sure that no one else has access to the `x` variable
13871391
in `main`, so we're good. But the call could also look like this:
13881392

13891393
~~~~
1390-
# fn myfunc(a: int, b: block()) {}
1394+
# fn myfunc(a: int, b: fn()) {}
13911395
# fn get_another_record() -> int { 1 }
13921396
# let x = 1;
13931397
myfunc(x, {|| x = get_another_record(); });
@@ -1408,7 +1412,7 @@ to pessimistically assume a value will get mutated, even though it is
14081412
not sure.
14091413

14101414
~~~~
1411-
fn for_each(v: [mutable @int], iter: block(@int)) {
1415+
fn for_each(v: [mutable @int], iter: fn(@int)) {
14121416
for elt in v { iter(elt); }
14131417
}
14141418
~~~~
@@ -1431,7 +1435,7 @@ with the `copy` operator:
14311435

14321436
~~~~
14331437
type mutrec = {mutable x: int};
1434-
fn for_each(v: [mutable mutrec], iter: block(mutrec)) {
1438+
fn for_each(v: [mutable mutrec], iter: fn(mutrec)) {
14351439
for elt in v { iter(copy elt); }
14361440
}
14371441
~~~~
@@ -1509,23 +1513,23 @@ defining such functions again and again for every type they apply to.
15091513
Thus, Rust allows functions and datatypes to have type parameters.
15101514

15111515
~~~~
1512-
fn for_rev<T>(v: [T], act: block(T)) {
1516+
fn for_rev<T>(v: [T], act: fn(T)) {
15131517
let i = vec::len(v);
15141518
while i > 0u {
15151519
i -= 1u;
15161520
act(v[i]);
15171521
}
15181522
}
15191523
1520-
fn map<T, U>(v: [T], f: block(T) -> U) -> [U] {
1524+
fn map<T, U>(v: [T], f: fn(T) -> U) -> [U] {
15211525
let acc = [];
15221526
for elt in v { acc += [f(elt)]; }
15231527
ret acc;
15241528
}
15251529
~~~~
15261530

15271531
When defined in this way, these functions can be applied to any type
1528-
of vector, as long as the type of the block's argument and the type of
1532+
of vector, as long as the type of the closure's argument and the type of
15291533
the vector's content agree with each other.
15301534

15311535
Inside a parameterized (generic) function, the names of the type
@@ -1635,7 +1639,7 @@ by value based on their type. There is one situation in which this is
16351639
difficult. If you try this program:
16361640

16371641
~~~~
1638-
# fn map(f: block(int) -> int, v: [int]) {}
1642+
# fn map(f: fn(int) -> int, v: [int]) {}
16391643
fn plus1(x: int) -> int { x + 1 }
16401644
map(plus1, [1, 2, 3]);
16411645
~~~~
@@ -1650,7 +1654,7 @@ pass to a generic higher-order function as being passed by pointer,
16501654
using the `&&` sigil:
16511655

16521656
~~~~
1653-
# fn map<T, U>(f: block(T) -> U, v: [T]) {}
1657+
# fn map<T, U>(f: fn(T) -> U, v: [T]) {}
16541658
fn plus1(&&x: int) -> int { x + 1 }
16551659
map(plus1, [1, 2, 3]);
16561660
~~~~
@@ -2027,11 +2031,11 @@ generalized sequence types:
20272031
~~~~
20282032
iface seq<T> {
20292033
fn len() -> uint;
2030-
fn iter(block(T));
2034+
fn iter(fn(T));
20312035
}
20322036
impl <T> of seq<T> for [T] {
20332037
fn len() -> uint { vec::len(self) }
2034-
fn iter(b: block(T)) {
2038+
fn iter(b: fn(T)) {
20352039
for elt in self { b(elt); }
20362040
}
20372041
}
@@ -2112,7 +2116,7 @@ to leave off the `of` clause.
21122116
# type currency = ();
21132117
# fn mk_currency(x: int, s: str) {}
21142118
impl int_util for int {
2115-
fn times(b: block(int)) {
2119+
fn times(b: fn(int)) {
21162120
let i = 0;
21172121
while i < self { b(i); i += 1; }
21182122
}

0 commit comments

Comments
 (0)