blob: 99be83c7df626b5b1a201e813ed2c2abb4eb5e09 [file] [log] [blame] [view] [edit]
<!-- Old heading. Do not remove or links may break. -->
<a id="closures-anonymous-functions-that-can-capture-their-environment"></a>
## Closures: Anonymous Functions that Capture Their Environment
Rust’s closures are anonymous functions you can save in a variable or pass as
arguments to other functions. You can create the closure in one place and then
call the closure elsewhere to evaluate it in a different context. Unlike
functions, closures can capture values from the scope in which they’re defined.
We’ll demonstrate how these closure features allow for code reuse and behavior
customization.
<!-- Old headings. Do not remove or links may break. -->
<a id="creating-an-abstraction-of-behavior-with-closures"></a>
<a id="refactoring-using-functions"></a>
<a id="refactoring-with-closures-to-store-code"></a>
### Capturing the Environment with Closures
We’ll first examine how we can use closures to capture values from the
environment they’re defined in for later use. Here’s the scenario: Every so
often, our t-shirt company gives away an exclusive, limited-edition shirt to
someone on our mailing list as a promotion. People on the mailing list can
optionally add their favorite color to their profile. If the person chosen for a
free shirt has their favorite color set, they get that color shirt. If the
person hasn’t specified a favorite color, they get whatever color the company
currently has the most of.
There are many ways to implement this. For this example, we’re going to use an
enum called `ShirtColor` that has the variants `Red` and `Blue` (limiting the
number of colors available for simplicity). We represent the company’s inventory
with an `Inventory` struct that has a field named `shirts` that contains a
`Vec<ShirtColor>` representing the shirt colors currently in stock. The method
`giveaway` defined on `Inventory` gets the optional shirt color preference of
the free shirt winner, and returns the shirt color the person will get. This
setup is shown in Listing 13-1:
<Listing number="13-1" file-name="src/main.rs" caption="Shirt company giveaway situation">
```rust,noplayground
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-01/src/main.rs}}
```
</Listing>
The `store` defined in `main` has two blue shirts and one red shirt remaining to
distribute for this limited-edition promotion. We call the `giveaway` method for
a user with a preference for a red shirt and a user without any preference.
Again, this code could be implemented in many ways, and here, to focus on
closures, we’ve stuck to concepts you’ve already learned except for the body of
the `giveaway` method that uses a closure. In the `giveaway` method, we get the
user preference as a parameter of type `Option<ShirtColor>` and call the
`unwrap_or_else` method on `user_preference`. The
[`unwrap_or_else` method on `Option<T>`][unwrap-or-else]<!-- ignore --> is
defined by the standard library. It takes one argument: a closure without any
arguments that returns a value `T` (the same type stored in the `Some` variant
of the `Option<T>`, in this case `ShirtColor`). If the `Option<T>` is the `Some`
variant, `unwrap_or_else` returns the value from within the `Some`. If the
`Option<T>` is the `None` variant, `unwrap_or_else` calls the closure and
returns the value returned by the closure.
We specify the closure expression `|| self.most_stocked()` as the argument to
`unwrap_or_else`. This is a closure that takes no parameters itself (if the
closure had parameters, they would appear between the two vertical bars). The
body of the closure calls `self.most_stocked()`. We’re defining the closure
here, and the implementation of `unwrap_or_else` will evaluate the closure later
if the result is needed.
Running this code prints:
```console
{{#include ../listings/ch13-functional-features/listing-13-01/output.txt}}
```
One interesting aspect here is that we’ve passed a closure that calls
`self.most_stocked()` on the current `Inventory` instance. The standard library
didn’t need to know anything about the `Inventory` or `ShirtColor` types we
defined, or the logic we want to use in this scenario. The closure captures an
immutable reference to the `self` `Inventory` instance and passes it with the
code we specify to the `unwrap_or_else` method. Functions, on the other hand,
are not able to capture their environment in this way.
### Closure Type Inference and Annotation
There are more differences between functions and closures. Closures don’t
usually require you to annotate the types of the parameters or the return value
like `fn` functions do. Type annotations are required on functions because the
types are part of an explicit interface exposed to your users. Defining this
interface rigidly is important for ensuring that everyone agrees on what types
of values a function uses and returns. Closures, on the other hand, aren’t used
in an exposed interface like this: they’re stored in variables and used without
naming them and exposing them to users of our library.
Closures are typically short and relevant only within a narrow context rather
than in any arbitrary scenario. Within these limited contexts, the compiler can
infer the types of the parameters and the return type, similar to how it’s able
to infer the types of most variables (there are rare cases where the compiler
needs closure type annotations too).
As with variables, we can add type annotations if we want to increase
explicitness and clarity at the cost of being more verbose than is strictly
necessary. Annotating the types for a closure would look like the definition
shown in Listing 13-2. In this example, we’re defining a closure and storing it
in a variable rather than defining the closure in the spot we pass it as an
argument as we did in Listing 13-1.
<Listing number="13-2" file-name="src/main.rs" caption="Adding optional type annotations of the parameter and return value types in the closure">
```rust
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-02/src/main.rs:here}}
```
</Listing>
With type annotations added, the syntax of closures looks more similar to the
syntax of functions. Here we define a function that adds 1 to its parameter and
a closure that has the same behavior, for comparison. We’ve added some spaces to
line up the relevant parts. This illustrates how closure syntax is similar to
function syntax except for the use of pipes and the amount of syntax that is
optional:
```rust,ignore
fn add_one_v1 (x: u32) -> u32 { x + 1 }
let add_one_v2 = |x: u32| -> u32 { x + 1 };
let add_one_v3 = |x| { x + 1 };
let add_one_v4 = |x| x + 1 ;
```
The first line shows a function definition, and the second line shows a fully
annotated closure definition. In the third line, we remove the type annotations
from the closure definition. In the fourth line, we remove the brackets, which
are optional because the closure body has only one expression. These are all
valid definitions that will produce the same behavior when they’re called. The
`add_one_v3` and `add_one_v4` lines require the closures to be evaluated to be
able to compile because the types will be inferred from their usage. This is
similar to `let v = Vec::new();` needing either type annotations or values of
some type to be inserted into the `Vec` for Rust to be able to infer the type.
For closure definitions, the compiler will infer one concrete type for each of
their parameters and for their return value. For instance, Listing 13-3 shows
the definition of a short closure that just returns the value it receives as a
parameter. This closure isn’t very useful except for the purposes of this
example. Note that we haven’t added any type annotations to the definition.
Because there are no type annotations, we can call the closure with any type,
which we’ve done here with `String` the first time. If we then try to call
`example_closure` with an integer, we’ll get an error.
<Listing number="13-3" file-name="src/main.rs" caption="Attempting to call a closure whose types are inferred with two different types">
```rust,ignore,does_not_compile
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-03/src/main.rs:here}}
```
</Listing>
The compiler gives us this error:
```console
{{#include ../listings/ch13-functional-features/listing-13-03/output.txt}}
```
The first time we call `example_closure` with the `String` value, the compiler
infers the type of `x` and the return type of the closure to be `String`. Those
types are then locked into the closure in `example_closure`, and we get a type
error when we next try to use a different type with the same closure.
### Capturing References or Moving Ownership
Closures can capture values from their environment in three ways, which directly
map to the three ways a function can take a parameter: borrowing immutably,
borrowing mutably, and taking ownership. The closure will decide which of these
to use based on what the body of the function does with the captured values.
In Listing 13-4, we define a closure that captures an immutable reference to the
vector named `list` because it only needs an immutable reference to print the
value:
<Listing number="13-4" file-name="src/main.rs" caption="Defining and calling a closure that captures an immutable reference">
```rust
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-04/src/main.rs}}
```
</Listing>
This example also illustrates that a variable can bind to a closure definition,
and we can later call the closure by using the variable name and parentheses as
if the variable name were a function name.
Because we can have multiple immutable references to `list` at the same time,
`list` is still accessible from the code before the closure definition, after
the closure definition but before the closure is called, and after the closure
is called. This code compiles, runs, and prints:
```console
{{#include ../listings/ch13-functional-features/listing-13-04/output.txt}}
```
Next, in Listing 13-5, we change the closure body so that it adds an element to
the `list` vector. The closure now captures a mutable reference:
<Listing number="13-5" file-name="src/main.rs" caption="Defining and calling a closure that captures a mutable reference">
```rust
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-05/src/main.rs}}
```
</Listing>
This code compiles, runs, and prints:
```console
{{#include ../listings/ch13-functional-features/listing-13-05/output.txt}}
```
Note that there’s no longer a `println!` between the definition and the call of
the `borrows_mutably` closure: when `borrows_mutably` is defined, it captures a
mutable reference to `list`. We don’t use the closure again after the closure is
called, so the mutable borrow ends. Between the closure definition and the
closure call, an immutable borrow to print isn’t allowed because no other
borrows are allowed when there’s a mutable borrow. Try adding a `println!` there
to see what error message you get!
If you want to force the closure to take ownership of the values it uses in the
environment even though the body of the closure doesn’t strictly need ownership,
you can use the `move` keyword before the parameter list.
This technique is mostly useful when passing a closure to a new thread to move
the data so that it’s owned by the new thread. We’ll discuss threads and why you
would want to use them in detail in Chapter 16 when we talk about concurrency,
but for now, let’s briefly explore spawning a new thread using a closure that
needs the `move` keyword. Listing 13-6 shows Listing 13-4 modified to print the
vector in a new thread rather than in the main thread:
<Listing number="13-6" file-name="src/main.rs" caption="Using `move` to force the closure for the thread to take ownership of `list`">
```rust
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-06/src/main.rs}}
```
</Listing>
We spawn a new thread, giving the thread a closure to run as an argument. The
closure body prints out the list. In Listing 13-4, the closure only captured
`list` using an immutable reference because that's the least amount of access to
`list` needed to print it. In this example, even though the closure body still
only needs an immutable reference, we need to specify that `list` should be
moved into the closure by putting the `move` keyword at the beginning of the
closure definition. The new thread might finish before the rest of the main
thread finishes, or the main thread might finish first. If the main thread
maintained ownership of `list` but ended before the new thread did and dropped
`list`, the immutable reference in the thread would be invalid. Therefore, the
compiler requires that `list` be moved into the closure given to the new thread
so the reference will be valid. Try removing the `move` keyword or using `list`
in the main thread after the closure is defined to see what compiler errors you
get!
<!-- Old headings. Do not remove or links may break. -->
<a id="storing-closures-using-generic-parameters-and-the-fn-traits"></a>
<a id="limitations-of-the-cacher-implementation"></a>
<a id="moving-captured-values-out-of-the-closure-and-the-fn-traits"></a>
### Moving Captured Values Out of Closures and the `Fn` Traits
Once a closure has captured a reference or captured ownership of a value from
the environment where the closure is defined (thus affecting what, if anything,
is moved _into_ the closure), the code in the body of the closure defines what
happens to the references or values when the closure is evaluated later (thus
affecting what, if anything, is moved _out of_ the closure). A closure body can
do any of the following: move a captured value out of the closure, mutate the
captured value, neither move nor mutate the value, or capture nothing from the
environment to begin with.
The way a closure captures and handles values from the environment affects which
traits the closure implements, and traits are how functions and structs can
specify what kinds of closures they can use. Closures will automatically
implement one, two, or all three of these `Fn` traits, in an additive fashion,
depending on how the closure’s body handles the values:
1. `FnOnce` applies to closures that can be called once. All closures implement
at least this trait, because all closures can be called. A closure that moves
captured values out of its body will only implement `FnOnce` and none of the
other `Fn` traits, because it can only be called once.
2. `FnMut` applies to closures that don’t move captured values out of their
body, but that might mutate the captured values. These closures can be called
more than once.
3. `Fn` applies to closures that don’t move captured values out of their body
and that don’t mutate captured values, as well as closures that capture
nothing from their environment. These closures can be called more than once
without mutating their environment, which is important in cases such as
calling a closure multiple times concurrently.
Let’s look at the definition of the `unwrap_or_else` method on `Option<T>` that
we used in Listing 13-1:
```rust,ignore
impl<T> Option<T> {
pub fn unwrap_or_else<F>(self, f: F) -> T
where
F: FnOnce() -> T
{
match self {
Some(x) => x,
None => f(),
}
}
}
```
Recall that `T` is the generic type representing the type of the value in the
`Some` variant of an `Option`. That type `T` is also the return type of the
`unwrap_or_else` function: code that calls `unwrap_or_else` on an
`Option<String>`, for example, will get a `String`.
Next, notice that the `unwrap_or_else` function has the additional generic type
parameter `F`. The `F` type is the type of the parameter named `f`, which is the
closure we provide when calling `unwrap_or_else`.
The trait bound specified on the generic type `F` is `FnOnce() -> T`, which
means `F` must be able to be called once, take no arguments, and return a `T`.
Using `FnOnce` in the trait bound expresses the constraint that `unwrap_or_else`
is only going to call `f` at most one time. In the body of `unwrap_or_else`, we
can see that if the `Option` is `Some`, `f` won’t be called. If the `Option` is
`None`, `f` will be called once. Because all closures implement `FnOnce`,
`unwrap_or_else` accepts all three kinds of closures and is as flexible as it
can be.
> Note: Functions can implement all three of the `Fn` traits too. If what we
> want to do doesn’t require capturing a value from the environment, we can use
> the name of a function rather than a closure where we need something that
> implements one of the `Fn` traits. For example, on an `Option<Vec<T>>` value,
> we could call `unwrap_or_else(Vec::new)` to get a new, empty vector if the
> value is `None`.
Now let’s look at the standard library method `sort_by_key` defined on slices,
to see how that differs from `unwrap_or_else` and why `sort_by_key` uses `FnMut`
instead of `FnOnce` for the trait bound. The closure gets one argument in the
form of a reference to the current item in the slice being considered, and
returns a value of type `K` that can be ordered. This function is useful when
you want to sort a slice by a particular attribute of each item. In Listing
13-7, we have a list of `Rectangle` instances and we use `sort_by_key` to order
them by their `width` attribute from low to high:
<Listing number="13-7" file-name="src/main.rs" caption="Using `sort_by_key` to order rectangles by width">
```rust
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-07/src/main.rs}}
```
</Listing>
This code prints:
```console
{{#include ../listings/ch13-functional-features/listing-13-07/output.txt}}
```
The reason `sort_by_key` is defined to take an `FnMut` closure is that it calls
the closure multiple times: once for each item in the slice. The closure
`|r|
r.width` doesn’t capture, mutate, or move out anything from its environment,
so it meets the trait bound requirements.
In contrast, Listing 13-8 shows an example of a closure that implements just the
`FnOnce` trait, because it moves a value out of the environment. The compiler
won’t let us use this closure with `sort_by_key`:
<Listing number="13-8" file-name="src/main.rs" caption="Attempting to use an `FnOnce` closure with `sort_by_key`">
```rust,ignore,does_not_compile
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-08/src/main.rs}}
```
</Listing>
This is a contrived, convoluted way (that doesn’t work) to try and count the
number of times `sort_by_key` calls the closure when sorting `list`. This code
attempts to do this counting by pushing `value`—a `String` from the closure’s
environment—into the `sort_operations` vector. The closure captures `value` then
moves `value` out of the closure by transferring ownership of `value` to the
`sort_operations` vector. This closure can be called once; trying to call it a
second time wouldn’t work because `value` would no longer be in the environment
to be pushed into `sort_operations` again! Therefore, this closure only
implements `FnOnce`. When we try to compile this code, we get this error that
`value` can’t be moved out of the closure because the closure must implement
`FnMut`:
```console
{{#include ../listings/ch13-functional-features/listing-13-08/output.txt}}
```
The error points to the line in the closure body that moves `value` out of the
environment. To fix this, we need to change the closure body so that it doesn’t
move values out of the environment. To count the number of times the closure is
called, keeping a counter in the environment and incrementing its value in the
closure body is a more straightforward way to calculate that. The closure in
Listing 13-9 works with `sort_by_key` because it is only capturing a mutable
reference to the `num_sort_operations` counter and can therefore be called more
than once:
<Listing number="13-9" file-name="src/main.rs" caption="Using an `FnMut` closure with `sort_by_key` is allowed">
```rust
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-09/src/main.rs}}
```
</Listing>
The `Fn` traits are important when defining or using functions or types that
make use of closures. In the next section, we’ll discuss iterators. Many
iterator methods take closure arguments, so keep these closure details in mind
as we continue!
[unwrap-or-else]: ../std/option/enum.Option.html#method.unwrap_or_else