-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tutorial doc #13676
Tutorial doc #13676
Changes from 6 commits
ad0cdd7
b580964
36f98fb
f7d2d58
af0dd16
f79571f
636f7d2
b3d7aa3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1717,23 +1717,35 @@ 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; | ||
|
||
// `fun` is an invalid definition | ||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you been previewing the results of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ...No, not really. I've been pushing it to GitHub and looking to see what they look like there. It looks fine on GitHub. I'll can test it next time I build it. |
||
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. | ||
|
||
let captured_var = 20; | ||
let closure = |arg| println!("captured_var={}, arg={}", captured_var, arg); | ||
fun(); // Still won't work | ||
closure(); // Prints: 3 | ||
|
||
call_closure_with_ten(closure); | ||
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 | ||
|
@@ -1742,13 +1754,41 @@ 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 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. | ||
Since a closure is an expression, the compiler can usually infer the argument and | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My mental model of how type inference works does not assign such special roles to "expression" vs "declaration" in terms of what is enabling inference here. (For example, if you look at ML, you will see that top-level function definitions ( There are good reasons we have inference for closures and not for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reasons there is a distinction may not be important here but when asking questions about closures on IRC, I learned a closure is similar in form to a function; yet I found the seemingly arbitrary disregard for function rules very perplexing. Labeling them as coming from different types of forms was very helpful. Hence I thought it worthwhile to include some explanation to stave off others future confusion. |
||
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: | ||
|
||
~~~~ {.ignore} | ||
// `fun` cannot infer the type of `x` so it must be provided because it is a function. | ||
fn fun (x: int) -> () { println!("{}", x) }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The semicolon at the end of this (As opposed to semi-colon at the end of the |
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this would be better phrased as " |
||
closure("String"); | ||
~~~~ | ||
|
||
The null arguments `()` are typically dropped so the end result | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. First of all, it would be better to say "The unit type Second: "typically dropped" is probably poor word choice. The word "drop" has a certain technical meaning in Rust related to destructors, and I'd prefer to avoid using the word "drop" in other contexts if we can avoid it. Maybe "are typically omitted" would be a better choice. Third: There are two separate details here that I think your presentation is conflating:
In your presentation, you mention that I would either revise the text to say that the types of closures are often inferred (and say nothing about |
||
is more compact. | ||
|
||
~~~~ | ||
let closure = |x| { println!("{}", x) }; | ||
|
||
closure(20); // Prints 20 | ||
~~~~ | ||
|
||
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 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the term assign is great here since it's a normal parameter being passed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The language there is poor in a number of respects, sorry I missed that earlier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(filed as PR #13946.)