From ad0cdd7081bf78c4b7a00344f6f2b2f113cf0957 Mon Sep 17 00:00:00 2001 From: mdinger Date: Fri, 18 Apr 2014 21:43:29 -0400 Subject: [PATCH 1/7] Be more clear about what this lambda expression does --- src/doc/guide-tasks.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/doc/guide-tasks.md b/src/doc/guide-tasks.md index 5dd58ccb61d9c..1f6d6854f7195 100644 --- a/src/doc/guide-tasks.md +++ b/src/doc/guide-tasks.md @@ -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!") ); ~~~~ From b5809644ad6ee87dc28afd8200fa8e12213f4f29 Mon Sep 17 00:00:00 2001 From: mdinger Date: Sat, 19 Apr 2014 03:21:01 -0400 Subject: [PATCH 2/7] Give more explanation when introducing closures --- src/doc/tutorial.md | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/doc/tutorial.md b/src/doc/tutorial.md index 336f296ba2503..ed70df7f3d37e 100644 --- a/src/doc/tutorial.md +++ b/src/doc/tutorial.md @@ -1719,19 +1719,41 @@ environment). For example, you couldn't write the following: ~~~~ {.ignore} let foo = 10; -fn bar() -> int { - return foo; // `bar` cannot refer to `foo` -} +// `bar` cannot refer to `foo` +fn bar() -> () { println!("{}", foo); } ~~~~ Rust also supports _closures_, functions that can access variables in -the enclosing scope. +the enclosing scope. Compare `foo` in these: + +~~~~ +fn bar() -> () { println!("{}", foo) }; // cannot reach enclosing scope +let closure = |foo| -> () { println!("{}", foo) }; // can reach enclosing scope +~~~~ + +Closures can be utilized in this fashion: ~~~~ -fn call_closure_with_ten(b: |int|) { b(10); } +// Create a nameless function and assign it to `closure`. +// It's sole argument is a yet unknown `foo` to be supplied +// by the caller. +let closure = |foo| -> () { println!("{}", foo) }; + +// 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); } -let captured_var = 20; -let closure = |arg| println!("captured_var={}, arg={}", captured_var, arg); +// The caller supplies `10` to the closure +// which prints out the value +call_closure_with_ten(closure); +~~~~ + +This can be simplified by removing null arguments: + +~~~~ +let closure = |foo| println!("{}", foo); +fn call_closure_with_ten(fun: |int|) { fun(10); } call_closure_with_ten(closure); ~~~~ From 36f98fb0bb9875bb7901c5e0cc757fde6ac0acdb Mon Sep 17 00:00:00 2001 From: mdinger Date: Sat, 19 Apr 2014 14:36:53 -0400 Subject: [PATCH 3/7] Demonstrate accessing external variable in first example --- src/doc/tutorial.md | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/doc/tutorial.md b/src/doc/tutorial.md index ed70df7f3d37e..6eee1a4d49a21 100644 --- a/src/doc/tutorial.md +++ b/src/doc/tutorial.md @@ -1717,27 +1717,32 @@ 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; -// `bar` cannot refer to `foo` -fn bar() -> () { println!("{}", foo); } +// `fun` cannot refer to `x` +fn fun() -> () { println!("{}", x); } ~~~~ Rust also supports _closures_, functions that can access variables in -the enclosing scope. Compare `foo` in these: +the enclosing scope. Compare `x` in these: ~~~~ -fn bar() -> () { println!("{}", foo) }; // cannot reach enclosing scope -let closure = |foo| -> () { println!("{}", foo) }; // can reach enclosing scope +let x = 3; + +// `fun` is an invalid definition +fn fun () -> () { println!("{}", x) }; // cannot reach enclosing scope +let closure = || -> () { println!("{}", x) }; // can reach enclosing scope + +fun(); // Still won't work +closure(); // Prints: 3 ~~~~ Closures can be utilized in this fashion: ~~~~ -// Create a nameless function and assign it to `closure`. -// It's sole argument is a yet unknown `foo` to be supplied -// by the caller. -let closure = |foo| -> () { println!("{}", foo) }; +// 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 @@ -1752,7 +1757,7 @@ call_closure_with_ten(closure); This can be simplified by removing null arguments: ~~~~ -let closure = |foo| println!("{}", foo); +let closure = |x| println!("{}", x); fn call_closure_with_ten(fun: |int|) { fun(10); } call_closure_with_ten(closure); From f7d2d5876c4d26dd5941c723aad2fc53c6fcc9aa Mon Sep 17 00:00:00 2001 From: mdinger Date: Sat, 19 Apr 2014 20:21:01 -0400 Subject: [PATCH 4/7] 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 From af0dd16b1bf05f0ef671f6c6eea8fa1627015d88 Mon Sep 17 00:00:00 2001 From: mdinger Date: Wed, 30 Apr 2014 09:10:15 -0400 Subject: [PATCH 5/7] Fix typo --- src/doc/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/tutorial.md b/src/doc/tutorial.md index eca765f9b9fa5..bd1e9b27ccb0d 100644 --- a/src/doc/tutorial.md +++ b/src/doc/tutorial.md @@ -1736,7 +1736,7 @@ let closure = || -> () { println!("{}", x) }; // can capture enclosing scope // `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 +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. From f79571f51355826308573f713357eb9e33be665a Mon Sep 17 00:00:00 2001 From: mdinger Date: Wed, 30 Apr 2014 09:29:53 -0400 Subject: [PATCH 6/7] Remove extra semicolons --- src/doc/tutorial.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/tutorial.md b/src/doc/tutorial.md index bd1e9b27ccb0d..35655707fef50 100644 --- a/src/doc/tutorial.md +++ b/src/doc/tutorial.md @@ -1731,11 +1731,11 @@ they try to access `x`: let x = 3; // `fun` is an invalid definition -fn fun () -> () { println!("{}", x) }; // cannot capture enclosing scope +fn fun () -> () { println!("{}", x) } // cannot capture enclosing scope let closure = || -> () { println!("{}", x) }; // can capture enclosing scope // `fun_arg` is an invalid definition -fn fun_arg (arg: int) -> () { println!("{}", arg + x) }; // cannot capture enclosing scope +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. From 636f7d23647a77463a07ca9619307b17f0750a14 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 30 Apr 2014 18:34:43 +0200 Subject: [PATCH 7/7] Suggested revisions to PR 13676. Most important: distinguish function decl sugar for omitting `-> ()` from type inference on closures. I also tried to add a couple more examples to further emphasize this distinction. Note that this sugar (of omitting `-> ()`) is actually already briefly mentioned in an earlier section, so it is a little tricky deciding whether to put more material here, or to move it up to the previous section. Other drive-by fixes: * Fix the line length of the code blocks to fit in the width provided in the rendered HTML * Some minor revisions to wording (e.g. try to clarify in some cases where a type mismatch is arising). --- src/doc/tutorial.md | 81 +++++++++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/src/doc/tutorial.md b/src/doc/tutorial.md index 35655707fef50..a33a8f6e70ac3 100644 --- a/src/doc/tutorial.md +++ b/src/doc/tutorial.md @@ -1731,13 +1731,13 @@ they try to access `x`: let x = 3; // `fun` is an invalid definition -fn fun () -> () { println!("{}", x) } // cannot capture enclosing scope -let closure = || -> () { println!("{}", x) }; // can capture enclosing scope +fn fun () -> () { println!("{}", x) } // cannot capture from enclosing scope +let closure = || -> () { println!("{}", x) }; // can capture from enclosing scope // `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 -// ^ +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. @@ -1752,43 +1752,68 @@ 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 `()`. +otherwise the block evaluates to `()`, the unit value. -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: +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} -// `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) }; +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 -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"); +// Error: mismatched types: expected `()` but found `uint` +fn badfun(x: int) { (x * x) as uint } ~~~~ -The null arguments `()` are typically dropped so the end result -is more compact. +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"); ~~~~ -let closure = |x| { println!("{}", x) }; -closure(20); // Prints 20 +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 }; ~~~~ -Here, in the rare case where the compiler needs assistance, -the arguments and return types may be annotated. +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(20)); // 400 -println!("{}", square(-20)); // 400 +println!("{}", square_explicit(20)); // 400 +println!("{}", square_infer(-20)); // 400 ~~~~ There are several forms of closure, each with its own role. The most