about summary refs log tree commit diff
path: root/src/doc/tutorial.md
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-05-04 14:21:52 -0700
committerbors <bors@rust-lang.org>2014-05-04 14:21:52 -0700
commit028159ead403c09efcaa2b0bb0738d33209c3950 (patch)
tree68e88ef840173435a8b532e936ff8288ce8935b6 /src/doc/tutorial.md
parent59569397fbcd0a9b7b3649cd9957cace2d6889d8 (diff)
parentb3d7aa39240f17ca8b496b31908da4c08a639555 (diff)
downloadrust-028159ead403c09efcaa2b0bb0738d33209c3950.tar.gz
rust-028159ead403c09efcaa2b0bb0738d33209c3950.zip
auto merge of #13676 : mdinger/rust/tutorial_doc, r=pnkfelix
Improve tutorial discussion of closures, e.g. with respect to type inference and variable capture.

Fix #13621 

---- original description follows

I'd like this pulled to master if possible but if not I'd appreciate comments on what I need to change.  I found the closures difficult to understand as they were so I tried to explain it so I would've had an easier time understanding it.  I think it's better at least, somewhat.

I don't know that everyone liked the `-> ()` I included but I thought explicit is best to aid understanding.  I thought it was much harder to understand than it should have been.

[EDIT] - Clicked too early.
This doesn't `make check` without errors on my Xubuntu on Virtualbox machine.  Not sure why.  I don't think I changed anything problematic.  I'll try `make check` on master tomorrow.

Opened https://github.com/mozilla/rust/issues/13621 regarding this.
Diffstat (limited to 'src/doc/tutorial.md')
-rw-r--r--src/doc/tutorial.md99
1 files changed, 82 insertions, 17 deletions
diff --git a/src/doc/tutorial.md b/src/doc/tutorial.md
index abb713f5e72..6abfa21962f 100644
--- a/src/doc/tutorial.md
+++ b/src/doc/tutorial.md
@@ -1720,38 +1720,103 @@ environment (sometimes referred to as "capturing" variables in their
 environment). For example, you couldn't write the following:
 
 ~~~~ {.ignore}
-let foo = 10;
+let x = 3;
 
-fn bar() -> int {
-   return foo; // `bar` cannot refer to `foo`
-}
+// `fun` cannot refer to `x`
+fn fun() -> () { println!("{}", x); }
 ~~~~
 
-Rust also supports _closures_, functions that can access variables in
-the enclosing scope.
+A _closure_ does support accessing the enclosing scope; below we will create
+2 _closures_ (nameless functions). Compare how `||` replaces `()` and how
+they try to access `x`:
 
-~~~~
-fn call_closure_with_ten(b: |int|) { b(10); }
+~~~~ {.ignore}
+let x = 3;
 
-let captured_var = 20;
-let closure = |arg| println!("captured_var={}, arg={}", captured_var, arg);
+// `fun` is an invalid definition
+fn  fun       () -> () { println!("{}", x) }  // cannot capture from enclosing scope
+let closure = || -> () { println!("{}", x) }; // can capture from enclosing scope
 
-call_closure_with_ten(closure);
+// `fun_arg` is an invalid definition
+fn  fun_arg       (arg: int) -> () { println!("{}", arg + x) }  // cannot capture
+let closure_arg = |arg: int| -> () { println!("{}", arg + x) }; // can capture
+//                      ^
+// Requires a type because the implementation needs to know which `+` to use.
+// In the future, the implementation may not need the help.
+
+fun();          // Still won't work
+closure();      // Prints: 3
+
+fun_arg(7);     // Still won't work
+closure_arg(7); // Prints: 10
 ~~~~
 
 Closures begin with the argument list between vertical bars and are followed by
 a single expression. Remember that a block, `{ <expr1>; <expr2>; ... }`, is
 considered a single expression: it evaluates to the result of the last
 expression it contains if that expression is not followed by a semicolon,
-otherwise the block evaluates to `()`.
+otherwise the block evaluates to `()`, the unit value.
+
+In general, return types and all argument types must be specified
+explicitly for function definitions.  (As previously mentioned in the
+[Functions section](#functions), omitting the return type from a
+function declaration is synonymous with an explicit declaration of
+return type unit, `()`.)
+
+~~~~ {.ignore}
+fn  fun   (x: int)         { println!("{}", x) } // this is same as saying `-> ()`
+fn  square(x: int) -> uint { (x * x) as uint }   // other return types are explicit
+
+// Error: mismatched types: expected `()` but found `uint`
+fn  badfun(x: int)         { (x * x) as uint }
+~~~~
+
+On the other hand, the compiler can usually infer both the argument
+and return types for a closure expression; therefore they are often
+omitted, since both a human reader and the compiler can deduce the
+types from the immediate context.  This is in contrast to function
+declarations, which require types to be specified and are not subject
+to type inference. Compare:
 
-The types of the arguments are generally omitted, as is the return type,
-because the compiler can almost always infer them. In the rare case where the
-compiler needs assistance, though, the arguments and return types may be
-annotated.
+~~~~ {.ignore}
+// `fun` as a function declaration cannot infer the type of `x`, so it must be provided
+fn  fun       (x: int) { println!("{}", x) }
+let closure = |x     | { println!("{}", x) }; // infers `x: int`, return type `()`
+
+// For closures, omitting a return type is *not* synonymous with `-> ()`
+let add_3   = |y     | { 3i + y }; // infers `y: int`, return type `int`.
+
+fun(10);            // Prints 10
+closure(20);        // Prints 20
+closure(add_3(30)); // Prints 33
 
+fun("String"); // Error: mismatched types
+
+// Error: mismatched types
+// inference already assigned `closure` the type `|int| -> ()`
+closure("String");
 ~~~~
-let square = |x: int| -> uint { (x * x) as uint };
+
+In cases where the compiler needs assistance, the arguments and return
+types may be annotated on closures, using the same notation as shown
+earlier.  In the example below, since different types provide an
+implementation for the operator `*`, the argument type for the `x`
+parameter must be explicitly provided.
+
+~~~~{.ignore}
+// Error: the type of `x` must be known to be used with `x * x`
+let square = |x     | -> uint { (x * x) as uint };
+~~~~
+
+In the corrected version, the argument type is explicitly annotated,
+while the return type can still be inferred.
+
+~~~~
+let square_explicit = |x: int| -> uint { (x * x) as uint };
+let square_infer    = |x: int|         { (x * x) as uint };
+
+println!("{}", square_explicit(20));  // 400
+println!("{}", square_infer(-20));    // 400
 ~~~~
 
 There are several forms of closure, each with its own role. The most