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.
This commit is contained in:
commit
028159ead4
2 changed files with 84 additions and 17 deletions
|
|
@ -101,6 +101,8 @@ fn print_message() { println!("I am running in a different task!"); }
|
|||
spawn(print_message);
|
||||
|
||||
// Print something more profound in a different task using a lambda expression
|
||||
// This uses the proc() keyword to assign to spawn a function with no name
|
||||
// That function will call println!(...) as requested
|
||||
spawn(proc() println!("I am also running in a different task!") );
|
||||
~~~~
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
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.
|
||||
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:
|
||||
|
||||
~~~~ {.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");
|
||||
~~~~
|
||||
|
||||
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 = |x: int| -> uint { (x * x) as uint };
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue