@@ -29,48 +29,86 @@ expected to return.
29
29
30
30
## Closures
31
31
32
- Normal Rust functions (declared with ` fn ` ) do not close over their
33
- environment. A ` lambda ` expression can be used to create a closure.
34
-
35
- fn make_plus_function(x: int) -> lambda(int) -> int {
36
- lambda(y: int) -> int { x + y }
37
- }
38
- let plus_two = make_plus_function(2);
39
- assert plus_two(3) == 5;
40
-
41
- A ` lambda ` function * copies* its environment (in this case, the
42
- binding for ` x ` ). It can not mutate the closed-over bindings, and will
43
- not see changes made to these variables after the ` lambda ` was
44
- evaluated. ` lambda ` s can be put in data structures and passed around
45
- without limitation.
46
-
47
- The type of a closure is ` lambda(args) -> type ` , as opposed to
48
- ` fn(args) -> type ` . The ` fn ` type stands for 'bare' functions, with no
49
- closure attached. Keep this in mind when writing higher-order
50
- functions.
51
-
52
- A different form of closure is the block. Blocks are written like they
53
- are in Ruby: ` {|x| x + y} ` , the formal parameters between pipes,
54
- followed by the function body. They are stack-allocated and properly
55
- close over their environment (they see updates to closed over
56
- variables, for example). But blocks can only be used in a limited set
57
- of circumstances. They can be passed to other functions, but not
58
- stored in data structures or returned.
59
-
60
- fn map_int(f: block(int) -> int, vec: [int]) -> [int] {
61
- let result = [];
62
- for i in vec { result += [f(i)]; }
63
- ret result;
64
- }
65
- map_int({|x| x + 1 }, [1, 2, 3]);
66
-
67
- The type of blocks is spelled ` block(args) -> type ` . Both closures and
68
- bare functions are automatically convert to ` block ` s when appropriate.
69
- Most higher-order functions should take their function arguments as
70
- ` block ` s.
71
-
72
- A block with no arguments is written ` {|| body(); } ` —you can not leave
73
- off the pipes.
32
+ Named rust functions, like those in the previous section, do not close
33
+ over their environment. Rust also includes support for closures, which
34
+ are anonymous functions that can access the variables that were in
35
+ scope at the time the closure was created. Closures are represented
36
+ as the pair of a function pointer (as in C) and the environment, which
37
+ is where the values of the closed over variables are stored. Rust
38
+ includes support for three varieties of closure, each with different
39
+ costs and capabilities:
40
+
41
+ - Stack closures (written ` block ` ) store their environment in the
42
+ stack frame of their creator; they are very lightweight but cannot
43
+ be stored in a data structure.
44
+ - Boxed closures (written ` fn@ ` ) store their environment in a
45
+ [ shared box] ( data#shared-box ) . These are good for storing within
46
+ data structures but cannot be sent to another task.
47
+ - Unique closures (written ` fn~ ` ) store their environment in a
48
+ [ unique box] ( data#unique-box ) . These are limited in the kinds of
49
+ data that they can close over so that they can be safely sent
50
+ between tasks. As with any unique pointer, copying a unique closure
51
+ results in a deep clone of the environment.
52
+
53
+ Both boxed closures and unique closures are subtypes of stack
54
+ closures, meaning that wherever a stack closure type appears, a boxed
55
+ or unique closure value can be used. This is due to the restrictions
56
+ placed on the use of stack closures, which ensure that all operations
57
+ on a stack closure are also safe on any kind of closure.
58
+
59
+ ### Working with closures
60
+
61
+ Closures are specified by writing an inline, anonymous function
62
+ declaration. For example, the following code creates a boxed closure:
63
+
64
+ let plus_two = fn@(x: int) -> int {
65
+ ret x + 2;
66
+ };
67
+
68
+ Creating a unique closure is very similar:
69
+
70
+ let plus_two_uniq = fn~(x: int) -> int {
71
+ ret x + 2;
72
+ };
73
+
74
+ Stack closures can be created in a similar way; however, because stack
75
+ closures literally point into their creator's stack frame, they can
76
+ only be used in a very specific way. Stack closures may be passed as
77
+ parameters and they may be called, but they may not be stored into
78
+ local variables or fields. Creating a stack closure can therefore be
79
+ done using a syntax like the following:
80
+
81
+ let doubled = vec::map([1, 2, 3], block(x: int) -> int {
82
+ x * 2
83
+ });
84
+
85
+ Here the ` vec::map() ` is the standard higher-order map function, which
86
+ applies the closure to each item in the vector and returns a new
87
+ vector containing the results.
88
+
89
+ ### Shorthand syntax
90
+
91
+ The syntax in the previous section was very explicit; it fully
92
+ specifies the kind of closure as well as the type of every parameter
93
+ and the return type. In practice, however, closures are often used as
94
+ parameters to functions, and all of these details can be inferred.
95
+ Therefore, we support a shorthand syntax similar to Ruby or Smalltalk
96
+ blocks, which looks as follows:
97
+
98
+ let doubled = vec::map([1, 2, 3], {|x| x*2});
99
+
100
+ Here the vertical bars after the open brace ` { ` indicate that this is
101
+ a closure. A list of parameters appears between the bars. The bars
102
+ must always be present: if there are no arguments, then simply write
103
+ ` {||...} ` .
104
+
105
+ As a further simplification, if the final parameter to a function is a
106
+ closure, the closure need not be placed within parenthesis.
107
+ Therefore, one could write
108
+
109
+ let doubled = vec::map([1, 2, 3]) {|x| x*2};
110
+
111
+ This form is often easier to parse as it involves less nesting.
74
112
75
113
## Binding
76
114
@@ -79,8 +117,8 @@ Partial application is done using the `bind` keyword in Rust.
79
117
let daynum = bind std::vec::position(_, ["mo", "tu", "we", "do",
80
118
"fr", "sa", "su"]);
81
119
82
- Binding a function produces a closure (` lambda ` type) in which some of
83
- the arguments to the bound function have already been provided.
120
+ Binding a function produces a boxed closure (` fn@ ` type) in which some
121
+ of the arguments to the bound function have already been provided.
84
122
` daynum ` will be a function taking a single string argument, and
85
123
returning the day of the week that string corresponds to (if any).
86
124
@@ -103,11 +141,47 @@ To run such an iteration, you could do this:
103
141
# fn for_rev(v: [int], act: block(int)) {}
104
142
for_rev([1, 2, 3], {|n| log n; });
105
143
106
- But Rust allows a more pleasant syntax for this situation, with the
107
- loop block moved out of the parenthesis and the final semicolon
108
- omitted :
144
+ Making use of the shorthand where a final closure argument can be
145
+ moved outside of the parentheses permits the following, which
146
+ looks quite like a normal loop :
109
147
110
148
# fn for_rev(v: [int], act: block(int)) {}
111
149
for_rev([1, 2, 3]) {|n|
112
150
log n;
113
151
}
152
+
153
+ Note that, because ` for_rev() ` returns unit type, no semicolon is
154
+ needed when the final closure is pulled outside of the parentheses.
155
+
156
+ ## Capture clauses
157
+
158
+ When creating a boxed or unique closure, the default is to copy in the
159
+ values of any closed over variables. But sometimes, particularly if a
160
+ value is large or expensive to copy, you would like to * move* the
161
+ value into the closure instead. Rust supports this via the use of a
162
+ capture clause, which lets you specify precisely whether each variable
163
+ used in the closure is copied or moved.
164
+
165
+ As an example, let's assume we had some type of unique tree type:
166
+
167
+ tag tree<T> = tree_rec<T>;
168
+ type tree_rec<T> = ~{left: option<tree>, right: option<tree>, val: T};
169
+
170
+ Now if we have a function like the following:
171
+
172
+ let some_tree: tree<T> = ...;
173
+ let some_closure = fn~() {
174
+ ... use some_tree in some way ...
175
+ };
176
+
177
+ Here the variable ` some_tree ` is used within the closure body, so a
178
+ deep copy will be performed. This can become quite expensive if the
179
+ tree is large. If we know that ` some_tree ` will not be used again,
180
+ we could avoid this expense by making use of a capture clause like so:
181
+
182
+ let some_tree: tree<T> = ...;
183
+ let some_closure = fn~[move some_tree]() {
184
+ ... use some_tree in some way ...
185
+ };
186
+
187
+ This is particularly useful when moving data into [ child tasks] ( task ) .
0 commit comments