about summary refs log tree commit diff
path: root/doc/tutorial/func.md
diff options
context:
space:
mode:
Diffstat (limited to 'doc/tutorial/func.md')
-rw-r--r--doc/tutorial/func.md168
1 files changed, 121 insertions, 47 deletions
diff --git a/doc/tutorial/func.md b/doc/tutorial/func.md
index 15811da04b3..f7397204f5c 100644
--- a/doc/tutorial/func.md
+++ b/doc/tutorial/func.md
@@ -29,48 +29,86 @@ expected to return.
 
 ## Closures
 
-Normal Rust functions (declared with `fn`) do not close over their
-environment. A `lambda` expression can be used to create a closure.
-
-    fn make_plus_function(x: int) -> lambda(int) -> int {
-        lambda(y: int) -> int { x + y }
-    }
-    let plus_two = make_plus_function(2);
-    assert plus_two(3) == 5;
-
-A `lambda` function *copies* its environment (in this case, the
-binding for `x`). It can not mutate the closed-over bindings, and will
-not see changes made to these variables after the `lambda` was
-evaluated. `lambda`s can be put in data structures and passed around
-without limitation.
-
-The type of a closure is `lambda(args) -> type`, as opposed to
-`fn(args) -> type`. The `fn` type stands for 'bare' functions, with no
-closure attached. Keep this in mind when writing higher-order
-functions.
-
-A different form of closure is the block. Blocks are written like they
-are in Ruby: `{|x| x + y}`, the formal parameters between pipes,
-followed by the function body. They are stack-allocated and properly
-close over their environment (they see updates to closed over
-variables, for example). But blocks can only be used in a limited set
-of circumstances. They can be passed to other functions, but not
-stored in data structures or returned.
-
-    fn map_int(f: block(int) -> int, vec: [int]) -> [int] {
-        let result = [];
-        for i in vec { result += [f(i)]; }
-        ret result;
-    }
-    map_int({|x| x + 1 }, [1, 2, 3]);
-
-The type of blocks is spelled `block(args) -> type`. Both closures and
-bare functions are automatically convert to `block`s when appropriate.
-Most higher-order functions should take their function arguments as
-`block`s.
-
-A block with no arguments is written `{|| body(); }`—you can not leave
-off the pipes.
+Named rust functions, like those in the previous section, do not close
+over their environment. Rust also includes support for closures, which
+are anonymous functions that can access the variables that were in
+scope at the time the closure was created.  Closures are represented
+as the pair of a function pointer (as in C) and the environment, which
+is where the values of the closed over variables are stored.  Rust
+includes support for three varieties of closure, each with different
+costs and capabilities:
+
+- Stack closures (written `block`) store their environment in the
+  stack frame of their creator; they are very lightweight but cannot
+  be stored in a data structure.
+- Boxed closures (written `fn@`) store their environment in a
+  [shared box](data#shared-box).  These are good for storing within
+  data structures but cannot be sent to another task.
+- Unique closures (written `fn~`) store their environment in a
+  [unique box](data#unique-box).  These are limited in the kinds of
+  data that they can close over so that they can be safely sent
+  between tasks.  As with any unique pointer, copying a unique closure
+  results in a deep clone of the environment.
+  
+Both boxed closures and unique closures are subtypes of stack
+closures, meaning that wherever a stack closure type appears, a boxed
+or unique closure value can be used.  This is due to the restrictions
+placed on the use of stack closures, which ensure that all operations
+on a stack closure are also safe on any kind of closure.
+
+### Working with closures
+
+Closures are specified by writing an inline, anonymous function
+declaration.  For example, the following code creates a boxed closure:
+
+    let plus_two = fn@(x: int) -> int {
+        ret x + 2;
+    };
+    
+Creating a unique closure is very similar:
+
+    let plus_two_uniq = fn~(x: int) -> int {
+        ret x + 2;
+    };
+    
+Stack closures can be created in a similar way; however, because stack
+closures literally point into their creator's stack frame, they can
+only be used in a very specific way.  Stack closures may be passed as
+parameters and they may be called, but they may not be stored into
+local variables or fields.  Creating a stack closure can therefore be
+done using a syntax like the following:
+
+    let doubled = vec::map([1, 2, 3], block(x: int) -> int {
+        x * 2
+    });
+    
+Here the `vec::map()` is the standard higher-order map function, which
+applies the closure to each item in the vector and returns a new
+vector containing the results.
+    
+### Shorthand syntax
+
+The syntax in the previous section was very explicit; it fully
+specifies the kind of closure as well as the type of every parameter
+and the return type.  In practice, however, closures are often used as
+parameters to functions, and all of these details can be inferred.
+Therefore, we support a shorthand syntax similar to Ruby or Smalltalk
+blocks, which looks as follows:
+
+    let doubled = vec::map([1, 2, 3], {|x| x*2});
+ 
+Here the vertical bars after the open brace `{` indicate that this is
+a closure.  A list of parameters appears between the bars.  The bars
+must always be present: if there are no arguments, then simply write
+`{||...}`.
+
+As a further simplification, if the final parameter to a function is a
+closure, the closure need not be placed within parenthesis.
+Therefore, one could write
+
+    let doubled = vec::map([1, 2, 3]) {|x| x*2};
+   
+This form is often easier to parse as it involves less nesting.  
 
 ## Binding
 
@@ -79,8 +117,8 @@ Partial application is done using the `bind` keyword in Rust.
     let daynum = bind std::vec::position(_, ["mo", "tu", "we", "do",
                                              "fr", "sa", "su"]);
 
-Binding a function produces a closure (`lambda` type) in which some of
-the arguments to the bound function have already been provided.
+Binding a function produces a boxed closure (`fn@` type) in which some
+of the arguments to the bound function have already been provided.
 `daynum` will be a function taking a single string argument, and
 returning the day of the week that string corresponds to (if any).
 
@@ -103,11 +141,47 @@ To run such an iteration, you could do this:
     # fn for_rev(v: [int], act: block(int)) {}
     for_rev([1, 2, 3], {|n| log n; });
 
-But Rust allows a more pleasant syntax for this situation, with the
-loop block moved out of the parenthesis and the final semicolon
-omitted:
+Making use of the shorthand where a final closure argument can be
+moved outside of the parentheses permits the following, which
+looks quite like a normal loop:
 
     # fn for_rev(v: [int], act: block(int)) {}
     for_rev([1, 2, 3]) {|n|
         log n;
     }
+
+Note that, because `for_rev()` returns unit type, no semicolon is
+needed when the final closure is pulled outside of the parentheses.
+
+## Capture clauses
+
+When creating a boxed or unique closure, the default is to copy in the
+values of any closed over variables.  But sometimes, particularly if a
+value is large or expensive to copy, you would like to *move* the
+value into the closure instead.  Rust supports this via the use of a
+capture clause, which lets you specify precisely whether each variable
+used in the closure is copied or moved.
+
+As an example, let's assume we had some type of unique tree type:
+
+    tag tree<T> = tree_rec<T>;
+    type tree_rec<T> = ~{left: option<tree>, right: option<tree>, val: T};
+
+Now if we have a function like the following:
+
+    let some_tree: tree<T> = ...;
+    let some_closure = fn~() {
+        ... use some_tree in some way ...
+    };
+    
+Here the variable `some_tree` is used within the closure body, so a
+deep copy will be performed.  This can become quite expensive if the
+tree is large.  If we know that `some_tree` will not be used again,
+we could avoid this expense by making use of a capture clause like so:
+
+    let some_tree: tree<T> = ...;
+    let some_closure = fn~[move some_tree]() {
+        ... use some_tree in some way ...
+    };
+
+This is particularly useful when moving data into [child tasks](task).