diff options
| author | bors <bors@rust-lang.org> | 2014-05-04 14:21:52 -0700 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2014-05-04 14:21:52 -0700 |
| commit | 028159ead403c09efcaa2b0bb0738d33209c3950 (patch) | |
| tree | 68e88ef840173435a8b532e936ff8288ce8935b6 /src/doc/tutorial.md | |
| parent | 59569397fbcd0a9b7b3649cd9957cace2d6889d8 (diff) | |
| parent | b3d7aa39240f17ca8b496b31908da4c08a639555 (diff) | |
| download | rust-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.md | 99 |
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 |
