Skip to content

Commit eddabcf

Browse files
committed
---
yaml --- r: 7167 b: refs/heads/master c: ef895b9 h: refs/heads/master i: 7165: b3de099 7163: b3ef55d 7159: 3f5ee0d 7151: 9cd3378 7135: bd316ab 7103: cbe0223 7039: 7f108c0 6911: f33e750 6655: 3940b4b 6143: 6a8ba95 v: v3
1 parent 9c087fa commit eddabcf

File tree

5 files changed

+203
-50
lines changed

5 files changed

+203
-50
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
---
2-
refs/heads/master: 441a42c5d2707ae93a8be3c6be5c426e7416e50b
2+
refs/heads/master: ef895b96320a9d5c64090bad1c8a147b0431eef1

trunk/doc/tutorial/data.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ All pointer types can be dereferenced with the `*` unary operator.
188188

189189
### Shared boxes
190190

191+
<a name="shared-box"></a>
192+
191193
Shared boxes are pointers to heap-allocated, reference counted memory.
192194
A cycle collector ensures that circular references do not result in
193195
memory leaks.
@@ -207,6 +209,8 @@ Shared boxes never cross task boundaries.
207209

208210
### Unique boxes
209211

212+
<a name="unique-box"></a>
213+
210214
In contrast to shared boxes, unique boxes are not reference counted.
211215
Instead, it is statically guaranteed that only a single owner of the
212216
box exists at any time.

trunk/doc/tutorial/func.md

Lines changed: 121 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -29,48 +29,86 @@ expected to return.
2929

3030
## Closures
3131

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.
74112

75113
## Binding
76114

@@ -79,8 +117,8 @@ Partial application is done using the `bind` keyword in Rust.
79117
let daynum = bind std::vec::position(_, ["mo", "tu", "we", "do",
80118
"fr", "sa", "su"]);
81119

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.
84122
`daynum` will be a function taking a single string argument, and
85123
returning the day of the week that string corresponds to (if any).
86124

@@ -103,11 +141,47 @@ To run such an iteration, you could do this:
103141
# fn for_rev(v: [int], act: block(int)) {}
104142
for_rev([1, 2, 3], {|n| log n; });
105143

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:
109147

110148
# fn for_rev(v: [int], act: block(int)) {}
111149
for_rev([1, 2, 3]) {|n|
112150
log n;
113151
}
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).

trunk/doc/tutorial/generic.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ without any sophistication).
8787

8888
## Kinds
8989

90+
<a name="kind"></a>
91+
9092
Perhaps surprisingly, the 'copy' (duplicate) operation is not defined
9193
for all Rust types. Resource types (types with destructors) can not be
9294
copied, and neither can any type whose copying would require copying a
@@ -100,7 +102,7 @@ unless you explicitly declare that type parameter to have copyable
100102
// This does not compile
101103
fn head_bad<T>(v: [T]) -> T { v[0] }
102104
// This does
103-
fn head<copy T>(v: [T]) -> T { v[0] }
105+
fn head<T:copy>(v: [T]) -> T { v[0] }
104106

105107
When instantiating a generic function, you can only instantiate it
106108
with types that fit its kinds. So you could not apply `head` to a

trunk/doc/tutorial/task.md

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,76 @@
11
# Tasks
22

3-
FIXME to be written
3+
Rust supports a system of lightweight tasks, similar to what is found
4+
in Erlang or other actor systems. Rust tasks communicate via messages
5+
and do not share data. However, it is possible to send data without
6+
copying it by making use of [unique boxes][uniques] (still, the data
7+
is owned by only one task at a time).
8+
9+
[uniques]: data.html#unique-box
10+
11+
NOTE: As Rust evolves, we expect the Task API to grow and change
12+
somewhat. The tutorial documents the API as it exists today.
13+
14+
## Spawning a task
15+
16+
Spawning a task is done using the various spawn functions in the
17+
module task. Let's begin with the simplest one, `task::spawn()`, and
18+
later move on to the others:
19+
20+
let some_value = 22;
21+
let child_task = task::spawn {||
22+
std::io::println("This executes in the child task.");
23+
std::io::println(#fmt("%d", some_value));
24+
};
25+
26+
The argument to `task::spawn()` is a [unique closure](func) of type
27+
`fn~()`, meaning that it takes no arguments and generates no return
28+
value. The effect of `task::spawn()` is to fire up a child task that
29+
will execute the closure in parallel with the creator. The result is
30+
a task id, here stored into the variable `child_task`.
31+
32+
## Ports and channels
33+
34+
Now that we have spawned a child task, it would be nice if we could
35+
communicate with it. This is done by creating a *port* with an
36+
associated *channel*. A port is simply a location to receive messages
37+
of a particular type. A channel is used to send messages to a port.
38+
For example, imagine we wish to perform two expensive computations
39+
in parallel. We might write something like:
40+
41+
let port = comm::port::<int>();
42+
let chan = comm::chan::<int>(port);
43+
let child_task = task::spawn {||
44+
let result = some_expensive_computation();
45+
comm::send(chan, result);
46+
};
47+
some_other_expensive_computation();
48+
let result = comm::recv(port);
49+
50+
Let's walk through this code line-by-line. The first line creates a
51+
port for receiving integers:
52+
53+
let port = comm::port::<int>();
54+
55+
This port is where we will receive the message from the child task
56+
once it is complete. The second line creates a channel for sending
57+
integers to the port `port`:
58+
59+
let chan = comm::chan::<int>(port);
60+
61+
The channel will be used by the child to send a message to the port.
62+
The next statement actually spawns the child:
63+
64+
let child_task = task::spawn {||
65+
let result = some_expensive_computation();
66+
comm::send(chan, result);
67+
};
68+
69+
This child will perform the expensive computation send the result
70+
over the channel. Finally, the parent continues by performing
71+
some other expensive computation and then waiting for the child's result
72+
to arrive on the port:
73+
74+
some_other_expensive_computation();
75+
let result = comm::recv(port);
76+

0 commit comments

Comments
 (0)