From f7d2d5876c4d26dd5941c723aad2fc53c6fcc9aa Mon Sep 17 00:00:00 2001 From: mdinger Date: Sat, 19 Apr 2014 20:21:01 -0400 Subject: [PATCH] Some more closure changes --- src/doc/tutorial.md | 79 ++++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 33 deletions(-) diff --git a/src/doc/tutorial.md b/src/doc/tutorial.md index 6eee1a4d49a21..eca765f9b9fa5 100644 --- a/src/doc/tutorial.md +++ b/src/doc/tutorial.md @@ -1723,59 +1723,72 @@ let x = 3; fn fun() -> () { println!("{}", x); } ~~~~ -Rust also supports _closures_, functions that can access variables in -the enclosing scope. Compare `x` in these: +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`: -~~~~ +~~~~ {.ignore} let x = 3; // `fun` is an invalid definition -fn fun () -> () { println!("{}", x) }; // cannot reach enclosing scope -let closure = || -> () { println!("{}", x) }; // can reach enclosing scope +fn fun () -> () { println!("{}", x) }; // cannot capture enclosing scope +let closure = || -> () { println!("{}", x) }; // can capture enclosing scope -fun(); // Still won't work -closure(); // Prints: 3 -~~~~ +// `fun_arg` is an invalid definition +fn fun_arg (arg: int) -> () { println!("{}", arg + x) }; // cannot capture enclosing scope +let closure_arg = |arg: int| -> () { println!("{}", arg + x) }; // Can capture enclosing scope +// ^ +// Requires a type because the implementation needs to know which `+` to use. +// In the future, the implementation may not need the help. -Closures can be utilized in this fashion: +fun(); // Still won't work +closure(); // Prints: 3 +fun_arg(7); // Still won't work +closure_arg(7); // Prints: 10 ~~~~ -// Create a nameless function and assign it to `closure`. It's sole -// argument is a yet unknown `x` to be supplied by the caller. -let closure = |x| -> () { println!("{}", x) }; -// Define `call_closure_with_ten` to take one argument and return null `()`. -// `fun` is a function which takes one `int` argument `|int|` and also returns -// null `()`. `|int|` defines the `fun` to be of type _closure_ -fn call_closure_with_ten(fun: |int| -> ()) -> () { fun(10); } +Closures begin with the argument list between vertical bars and are followed by +a single expression. Remember that a block, `{ ; ; ... }`, 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 `()`. -// The caller supplies `10` to the closure -// which prints out the value -call_closure_with_ten(closure); -~~~~ +Since a closure is an expression, the compiler can usually infer the argument and +return types; so they are often omitted. This is in contrast to a function which +is a declaration and _not_ an expression. Declarations require the types to be +specified and carry no inference. Compare: -This can be simplified by removing null arguments: +~~~~ {.ignore} +// `fun` cannot infer the type of `x` so it must be provided because it is a function. +fn fun (x: int) -> () { println!("{}", x) }; +let closure = |x | -> () { println!("{}", x) }; +fun(10); // Prints 10 +closure(20); // Prints 20 + +fun("String"); // Error: wrong type +// Error: This type is different from when `x` was originally evaluated +closure("String"); ~~~~ -let closure = |x| println!("{}", x); -fn call_closure_with_ten(fun: |int|) { fun(10); } -call_closure_with_ten(closure); +The null arguments `()` are typically dropped so the end result +is more compact. + ~~~~ +let closure = |x| { println!("{}", x) }; -Closures begin with the argument list between vertical bars and are followed by -a single expression. Remember that a block, `{ ; ; ... }`, 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 `()`. +closure(20); // Prints 20 +~~~~ -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. +Here, in the rare case where the compiler needs assistance, +the arguments and return types may be annotated. ~~~~ let square = |x: int| -> uint { (x * x) as uint }; + +println!("{}", square(20)); // 400 +println!("{}", square(-20)); // 400 ~~~~ There are several forms of closure, each with its own role. The most