parent
32a8567ea4
commit
8e420e07d9
@ -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,14 @@ 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, types within 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 +64,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 it's enclosing scope in
|
||||
addition to parameters and local bindings. It looks like this:
|
||||
|
||||
```rust
|
||||
let num = 5;
|
||||
@ -197,9 +198,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 +290,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
|
||||
|
2
src/llvm
2
src/llvm
@ -1 +1 @@
|
||||
Subproject commit 62ad301a2407a7aca50c1d5120c63597d676d29f
|
||||
Subproject commit 2e9f0d21fe321849a4759a01fc28eae82ef196d6
|
Loading…
Reference in New Issue
Block a user