Rollup merge of #28856 - chills42:master, r=steveklabnik
This is to address issue #28803 by improving some of the references to closures, to explain what they are more clearly, while hopefully still being concise. r? @steveklabnik
This commit is contained in:
commit
f688c0e711
1 changed files with 24 additions and 21 deletions
|
|
@ -1,9 +1,10 @@
|
|||
% Closures
|
||||
|
||||
Rust not only has named functions, but anonymous functions as well. Anonymous
|
||||
functions that have an associated environment are called ‘closures’, because they
|
||||
close over an environment. Rust has a really great implementation of them, as
|
||||
we’ll see.
|
||||
Sometimes it is useful to wrap up a function and _free variables_ for better
|
||||
clarity and reuse. The free variables that can be used come from the
|
||||
enclosing scope and are ‘closed over’ when used in the function. From this, we
|
||||
get the name ‘closures’ and Rust provides a really great implementation of
|
||||
them, as we’ll see.
|
||||
|
||||
# Syntax
|
||||
|
||||
|
|
@ -34,7 +35,7 @@ assert_eq!(4, plus_two(2));
|
|||
```
|
||||
|
||||
You’ll notice a few things about closures that are a bit different from regular
|
||||
functions defined with `fn`. The first is that we did not need to
|
||||
named functions defined with `fn`. The first is that we did not need to
|
||||
annotate the types of arguments the closure takes or the values it returns. We
|
||||
can:
|
||||
|
||||
|
|
@ -44,14 +45,15 @@ let plus_one = |x: i32| -> i32 { x + 1 };
|
|||
assert_eq!(2, plus_one(1));
|
||||
```
|
||||
|
||||
But we don’t have to. Why is this? Basically, it was chosen for ergonomic reasons.
|
||||
While specifying the full type for named functions is helpful with things like
|
||||
documentation and type inference, the types of closures are rarely documented
|
||||
since they’re anonymous, and they don’t cause the kinds of error-at-a-distance
|
||||
problems that inferring named function types can.
|
||||
But we don’t have to. Why is this? Basically, it was chosen for ergonomic
|
||||
reasons. While specifying the full type for named functions is helpful with
|
||||
things like documentation and type inference, the full type signatures of
|
||||
closures are rarely documented since they’re anonymous, and they don’t cause
|
||||
the kinds of error-at-a-distance problems that inferring named function types
|
||||
can.
|
||||
|
||||
The second is that the syntax is similar, but a bit different. I’ve added spaces
|
||||
here for easier comparison:
|
||||
The second is that the syntax is similar, but a bit different. I’ve added
|
||||
spaces here for easier comparison:
|
||||
|
||||
```rust
|
||||
fn plus_one_v1 (x: i32) -> i32 { x + 1 }
|
||||
|
|
@ -63,8 +65,8 @@ Small differences, but they’re similar.
|
|||
|
||||
# Closures and their environment
|
||||
|
||||
Closures are called such because they ‘close over their environment’. It
|
||||
looks like this:
|
||||
The environment for a closure can include bindings from its enclosing scope in
|
||||
addition to parameters and local bindings. It looks like this:
|
||||
|
||||
```rust
|
||||
let num = 5;
|
||||
|
|
@ -197,9 +199,10 @@ frame. Without `move`, a closure may be tied to the stack frame that created
|
|||
it, while a `move` closure is self-contained. This means that you cannot
|
||||
generally return a non-`move` closure from a function, for example.
|
||||
|
||||
But before we talk about taking and returning closures, we should talk some more
|
||||
about the way that closures are implemented. As a systems language, Rust gives
|
||||
you tons of control over what your code does, and closures are no different.
|
||||
But before we talk about taking and returning closures, we should talk some
|
||||
more about the way that closures are implemented. As a systems language, Rust
|
||||
gives you tons of control over what your code does, and closures are no
|
||||
different.
|
||||
|
||||
# Closure implementation
|
||||
|
||||
|
|
@ -288,9 +291,9 @@ isn’t interesting. The next part is:
|
|||
# some_closure(1) }
|
||||
```
|
||||
|
||||
Because `Fn` is a trait, we can bound our generic with it. In this case, our closure
|
||||
takes a `i32` as an argument and returns an `i32`, and so the generic bound we use
|
||||
is `Fn(i32) -> i32`.
|
||||
Because `Fn` is a trait, we can bound our generic with it. In this case, our
|
||||
closure takes a `i32` as an argument and returns an `i32`, and so the generic
|
||||
bound we use is `Fn(i32) -> i32`.
|
||||
|
||||
There’s one other key point here: because we’re bounding a generic with a
|
||||
trait, this will get monomorphized, and therefore, we’ll be doing static
|
||||
|
|
@ -452,7 +455,7 @@ autogenerated name.
|
|||
The error also points out that the return type is expected to be a reference,
|
||||
but what we are trying to return is not. Further, we cannot directly assign a
|
||||
`'static` lifetime to an object. So we'll take a different approach and return
|
||||
a "trait object" by `Box`ing up the `Fn`. This _almost_ works:
|
||||
a ‘trait object’ by `Box`ing up the `Fn`. This _almost_ works:
|
||||
|
||||
```rust,ignore
|
||||
fn factory() -> Box<Fn(i32) -> i32> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue