Skip to content

Commit 028159e

Browse files
committed
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 #13621 regarding this.
2 parents 5956939 + b3d7aa3 commit 028159e

File tree

2 files changed

+84
-17
lines changed

2 files changed

+84
-17
lines changed

src/doc/guide-tasks.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ fn print_message() { println!("I am running in a different task!"); }
101101
spawn(print_message);
102102
103103
// Print something more profound in a different task using a lambda expression
104+
// This uses the proc() keyword to assign to spawn a function with no name
105+
// That function will call println!(...) as requested
104106
spawn(proc() println!("I am also running in a different task!") );
105107
~~~~
106108

src/doc/tutorial.md

Lines changed: 82 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,38 +1720,103 @@ environment (sometimes referred to as "capturing" variables in their
17201720
environment). For example, you couldn't write the following:
17211721
17221722
~~~~ {.ignore}
1723-
let foo = 10;
1723+
let x = 3;
17241724
1725-
fn bar() -> int {
1726-
return foo; // `bar` cannot refer to `foo`
1727-
}
1725+
// `fun` cannot refer to `x`
1726+
fn fun() -> () { println!("{}", x); }
17281727
~~~~
17291728
1730-
Rust also supports _closures_, functions that can access variables in
1731-
the enclosing scope.
1729+
A _closure_ does support accessing the enclosing scope; below we will create
1730+
2 _closures_ (nameless functions). Compare how `||` replaces `()` and how
1731+
they try to access `x`:
17321732
1733-
~~~~
1734-
fn call_closure_with_ten(b: |int|) { b(10); }
1733+
~~~~ {.ignore}
1734+
let x = 3;
17351735
1736-
let captured_var = 20;
1737-
let closure = |arg| println!("captured_var={}, arg={}", captured_var, arg);
1736+
// `fun` is an invalid definition
1737+
fn fun () -> () { println!("{}", x) } // cannot capture from enclosing scope
1738+
let closure = || -> () { println!("{}", x) }; // can capture from enclosing scope
17381739
1739-
call_closure_with_ten(closure);
1740+
// `fun_arg` is an invalid definition
1741+
fn fun_arg (arg: int) -> () { println!("{}", arg + x) } // cannot capture
1742+
let closure_arg = |arg: int| -> () { println!("{}", arg + x) }; // can capture
1743+
// ^
1744+
// Requires a type because the implementation needs to know which `+` to use.
1745+
// In the future, the implementation may not need the help.
1746+
1747+
fun(); // Still won't work
1748+
closure(); // Prints: 3
1749+
1750+
fun_arg(7); // Still won't work
1751+
closure_arg(7); // Prints: 10
17401752
~~~~
17411753
17421754
Closures begin with the argument list between vertical bars and are followed by
17431755
a single expression. Remember that a block, `{ <expr1>; <expr2>; ... }`, is
17441756
considered a single expression: it evaluates to the result of the last
17451757
expression it contains if that expression is not followed by a semicolon,
1746-
otherwise the block evaluates to `()`.
1758+
otherwise the block evaluates to `()`, the unit value.
1759+
1760+
In general, return types and all argument types must be specified
1761+
explicitly for function definitions. (As previously mentioned in the
1762+
[Functions section](#functions), omitting the return type from a
1763+
function declaration is synonymous with an explicit declaration of
1764+
return type unit, `()`.)
1765+
1766+
~~~~ {.ignore}
1767+
fn fun (x: int) { println!("{}", x) } // this is same as saying `-> ()`
1768+
fn square(x: int) -> uint { (x * x) as uint } // other return types are explicit
1769+
1770+
// Error: mismatched types: expected `()` but found `uint`
1771+
fn badfun(x: int) { (x * x) as uint }
1772+
~~~~
1773+
1774+
On the other hand, the compiler can usually infer both the argument
1775+
and return types for a closure expression; therefore they are often
1776+
omitted, since both a human reader and the compiler can deduce the
1777+
types from the immediate context. This is in contrast to function
1778+
declarations, which require types to be specified and are not subject
1779+
to type inference. Compare:
17471780
1748-
The types of the arguments are generally omitted, as is the return type,
1749-
because the compiler can almost always infer them. In the rare case where the
1750-
compiler needs assistance, though, the arguments and return types may be
1751-
annotated.
1781+
~~~~ {.ignore}
1782+
// `fun` as a function declaration cannot infer the type of `x`, so it must be provided
1783+
fn fun (x: int) { println!("{}", x) }
1784+
let closure = |x | { println!("{}", x) }; // infers `x: int`, return type `()`
1785+
1786+
// For closures, omitting a return type is *not* synonymous with `-> ()`
1787+
let add_3 = |y | { 3i + y }; // infers `y: int`, return type `int`.
1788+
1789+
fun(10); // Prints 10
1790+
closure(20); // Prints 20
1791+
closure(add_3(30)); // Prints 33
17521792
1793+
fun("String"); // Error: mismatched types
1794+
1795+
// Error: mismatched types
1796+
// inference already assigned `closure` the type `|int| -> ()`
1797+
closure("String");
17531798
~~~~
1754-
let square = |x: int| -> uint { (x * x) as uint };
1799+
1800+
In cases where the compiler needs assistance, the arguments and return
1801+
types may be annotated on closures, using the same notation as shown
1802+
earlier. In the example below, since different types provide an
1803+
implementation for the operator `*`, the argument type for the `x`
1804+
parameter must be explicitly provided.
1805+
1806+
~~~~{.ignore}
1807+
// Error: the type of `x` must be known to be used with `x * x`
1808+
let square = |x | -> uint { (x * x) as uint };
1809+
~~~~
1810+
1811+
In the corrected version, the argument type is explicitly annotated,
1812+
while the return type can still be inferred.
1813+
1814+
~~~~
1815+
let square_explicit = |x: int| -> uint { (x * x) as uint };
1816+
let square_infer = |x: int| { (x * x) as uint };
1817+
1818+
println!("{}", square_explicit(20)); // 400
1819+
println!("{}", square_infer(-20)); // 400
17551820
~~~~
17561821
17571822
There are several forms of closure, each with its own role. The most

0 commit comments

Comments
 (0)