]> git.proxmox.com Git - rustc.git/blame - src/doc/book/second-edition/src/ch13-01-closures.md
New upstream version 1.25.0+dfsg1
[rustc.git] / src / doc / book / second-edition / src / ch13-01-closures.md
CommitLineData
ff7c6d11 1## Closures: Anonymous Functions that Can Capture Their Environment
7cac9316 2
ea8adc8c
XL
3Rust’s *closures* are anonymous functions you can save in a variable or pass as
4arguments to other functions. You can create the closure in one place, and then
5call the closure to evaluate it in a different context. Unlike functions,
ff7c6d11
XL
6closures can capture values from the scope in which they’re called. We’ll
7demonstrate how these closure features allow for code reuse and behavior
8customization.
7cac9316 9
ff7c6d11 10### Creating an Abstraction of Behavior with Closures
7cac9316 11
ea8adc8c 12Let’s work on an example of a situation in which it’s useful to store a closure
ff7c6d11
XL
13to be executed at a later time. Along the way, we’ll talk about the syntax of
14closures, type inference, and traits.
ea8adc8c 15
ff7c6d11 16Consider this hypothetical situation: we work at a startup that’s making an app
ea8adc8c
XL
17to generate custom exercise workout plans. The backend is written in Rust, and
18the algorithm that generates the workout plan takes into account many different
ff7c6d11 19factors, such as the app user’s age, body mass index, preferences, recent
ea8adc8c
XL
20workouts, and an intensity number they specify. The actual algorithm used isn’t
21important in this example; what’s important is that this calculation takes a
ff7c6d11
XL
22few seconds. We want to call this algorithm only when we need to and only call
23it once, so we don’t make the user wait more than necessary.
ea8adc8c
XL
24
25We’ll simulate calling this hypothetical algorithm with the
26`simulated_expensive_calculation` function shown in Listing 13-1, which will
27print `calculating slowly...`, wait for two seconds, and then return whatever
28number we passed in:
cc61c64b 29
7cac9316
XL
30<span class="filename">Filename: src/main.rs</span>
31
32```rust
33use std::thread;
34use std::time::Duration;
35
ea8adc8c 36fn simulated_expensive_calculation(intensity: u32) -> u32 {
7cac9316
XL
37 println!("calculating slowly...");
38 thread::sleep(Duration::from_secs(2));
39 intensity
40}
41```
42
ea8adc8c
XL
43<span class="caption">Listing 13-1: A function to stand in for a hypothetical
44calculation that takes about two seconds to run</span>
7cac9316 45
ff7c6d11
XL
46Next is the `main` function that contains the parts of the workout app
47important for this example. This function represents the code that the app will
48call when a user asks for a workout plan. Because the interaction with the
49app’s frontend isn’t relevant to the use of closures, we’ll hardcode values
ea8adc8c 50representing inputs to our program and print the outputs.
7cac9316 51
ea8adc8c 52The required inputs are:
7cac9316 53
ff7c6d11
XL
54* *An intensity number from the user*, which is specified when they request
55 a workout to indicate whether they want a low-intensity workout or a
56 high-intensity workout.
57* *A random number* that will generate some variety in the workout plans.
7cac9316 58
ff7c6d11
XL
59The output will be the recommended workout plan. Listing 13-2 shows the `main`
60function we’ll use:
cc61c64b
XL
61
62<span class="filename">Filename: src/main.rs</span>
63
64```rust
65fn main() {
7cac9316
XL
66 let simulated_user_specified_value = 10;
67 let simulated_random_number = 7;
cc61c64b 68
ea8adc8c
XL
69 generate_workout(
70 simulated_user_specified_value,
71 simulated_random_number
72 );
7cac9316 73}
ea8adc8c 74# fn generate_workout(intensity: u32, random_number: u32) {}
7cac9316
XL
75```
76
ea8adc8c
XL
77<span class="caption">Listing 13-2: A `main` function with hardcoded values to
78simulate user input and random number generation</span>
79
80We’ve hardcoded the variable `simulated_user_specified_value` to 10 and the
81variable `simulated_random_number` to 7 for simplicity’s sake; in an actual
ff7c6d11
XL
82program, we’d get the intensity number from the app frontend and we’d use the
83`rand` crate to generate a random number, as we did in the Guessing Game
ea8adc8c
XL
84example in Chapter 2. The `main` function calls a `generate_workout` function
85with the simulated input values.
7cac9316 86
ff7c6d11
XL
87Now that we have the context, let’s get to the algorithm. The
88`generate_workout` function in Listing 13-3 contains the business logic of the
89app that we’re most concerned with in this example. The rest of the code
90changes in this example will be made to this function:
cc61c64b 91
7cac9316
XL
92<span class="filename">Filename: src/main.rs</span>
93
94```rust
95# use std::thread;
96# use std::time::Duration;
97#
ea8adc8c 98# fn simulated_expensive_calculation(num: u32) -> u32 {
7cac9316
XL
99# println!("calculating slowly...");
100# thread::sleep(Duration::from_secs(2));
101# num
102# }
103#
ea8adc8c 104fn generate_workout(intensity: u32, random_number: u32) {
7cac9316
XL
105 if intensity < 25 {
106 println!(
107 "Today, do {} pushups!",
108 simulated_expensive_calculation(intensity)
109 );
110 println!(
111 "Next, do {} situps!",
112 simulated_expensive_calculation(intensity)
113 );
114 } else {
115 if random_number == 3 {
116 println!("Take a break today! Remember to stay hydrated!");
117 } else {
118 println!(
119 "Today, run for {} minutes!",
120 simulated_expensive_calculation(intensity)
ea8adc8c 121 );
7cac9316
XL
122 }
123 }
cc61c64b
XL
124}
125```
126
ea8adc8c
XL
127<span class="caption">Listing 13-3: The business logic that prints the workout
128plans based on the inputs and calls to the `simulated_expensive_calculation`
129function</span>
7cac9316
XL
130
131The code in Listing 13-3 has multiple calls to the slow calculation function.
132The first `if` block calls `simulated_expensive_calculation` twice, the `if`
ea8adc8c
XL
133inside the outer `else` doesn’t call it at all, and the code inside the
134second `else` case calls it once.
7cac9316 135
ff7c6d11
XL
136<!-- NEXT PARAGRAPH WRAPPED WEIRD INTENTIONALLY SEE #199 -->
137
138The desired behavior of the `generate_workout` function is to first check
139whether the user wants a low-intensity workout (indicated by a number less
140than 25) or a high-intensity workout (a number of 25 or greater).
ea8adc8c 141
ff7c6d11
XL
142Low-intensity workout plans will recommend a number of push-ups and sit-ups
143based on the complex algorithm we’re simulating.
7cac9316 144
ff7c6d11 145If the user wants a high-intensity workout, there’s some additional logic: if
7cac9316 146the value of the random number generated by the app happens to be 3, the app
ea8adc8c
XL
147will recommend a break and hydration. If not, the user will get a number of
148minutes of running based on the complex algorithm.
149
150The data science team has let us know that we’ll have to make some changes to
151the way we call the algorithm in the future. To simplify the update when those
ff7c6d11
XL
152changes happen, we want to refactor this code so it calls the
153`simulated_expensive_calculation` function only once. We also want to cut the
154place where we’re currently unnecessarily calling the function twice without
155adding any other calls to that function in the process. That is, we don’t want
156to call it if the result isn’t needed, and we still want to call it only once.
ea8adc8c
XL
157
158#### Refactoring Using Functions
159
ff7c6d11
XL
160We could restructure the workout program in many ways. First, we’ll try
161extracting the duplicated call to the `expensive_calculation` function into
162a variable, as shown in Listing 13-4:
7cac9316
XL
163
164<span class="filename">Filename: src/main.rs</span>
165
166```rust
167# use std::thread;
168# use std::time::Duration;
169#
ea8adc8c 170# fn simulated_expensive_calculation(num: u32) -> u32 {
7cac9316
XL
171# println!("calculating slowly...");
172# thread::sleep(Duration::from_secs(2));
173# num
174# }
175#
ea8adc8c 176fn generate_workout(intensity: u32, random_number: u32) {
7cac9316
XL
177 let expensive_result =
178 simulated_expensive_calculation(intensity);
179
180 if intensity < 25 {
181 println!(
182 "Today, do {} pushups!",
183 expensive_result
184 );
185 println!(
186 "Next, do {} situps!",
187 expensive_result
188 );
189 } else {
190 if random_number == 3 {
191 println!("Take a break today! Remember to stay hydrated!");
192 } else {
193 println!(
194 "Today, run for {} minutes!",
195 expensive_result
ea8adc8c 196 );
7cac9316
XL
197 }
198 }
199}
200```
cc61c64b 201
7cac9316 202<span class="caption">Listing 13-4: Extracting the calls to
ea8adc8c
XL
203`simulated_expensive_calculation` to one place and storing the result in the
204`expensive_result` variable</span>
7cac9316
XL
205
206This change unifies all the calls to `simulated_expensive_calculation` and
ff7c6d11
XL
207solves the problem of the first `if` block unnecessarily calling the function
208twice. Unfortunately, we’re now calling this function and waiting for the
209result in all cases, which includes the inner `if` block that doesn’t use the
210result value at all.
7cac9316 211
ea8adc8c
XL
212We want to define code in one place in our program, but only *execute* that
213code where we actually need the result. This is a use case for closures!
7cac9316 214
ff7c6d11 215#### Refactoring with Closures to Store Code
7cac9316
XL
216
217Instead of always calling the `simulated_expensive_calculation` function before
ea8adc8c
XL
218the `if` blocks, we can define a closure and store the *closure* in a variable
219rather than storing the result, as shown in Listing 13-5. We can actually move
3b2f2976 220the whole body of `simulated_expensive_calculation` within the closure we’re
7cac9316 221introducing here:
cc61c64b
XL
222
223<span class="filename">Filename: src/main.rs</span>
224
225```rust
7cac9316
XL
226# use std::thread;
227# use std::time::Duration;
228#
229let expensive_closure = |num| {
230 println!("calculating slowly...");
231 thread::sleep(Duration::from_secs(2));
232 num
233};
234# expensive_closure(5);
235```
cc61c64b 236
ea8adc8c
XL
237<span class="caption">Listing 13-5: Defining a closure and storing it in the
238`expensive_closure` variable</span>
239
240The closure definition comes after the `=` to assign it to the variable
241`expensive_closure`. To define a closure, we start with a pair of vertical
242pipes (`|`), inside which we specify the parameters to the closure; this syntax
243was chosen because of its similarity to closure definitions in Smalltalk and
ff7c6d11 244Ruby. This closure has one parameter named `num`: if we had more than one
ea8adc8c
XL
245parameter, we would separate them with commas, like `|param1, param2|`.
246
247After the parameters, we place curly brackets that hold the body of the
248closure—these are optional if the closure body is a single expression. The end
249of the closure, after the curly brackets, needs a semicolon to complete the
250`let` statement. The value returned from the last line in the closure body
ff7c6d11 251(`num`) will be the value returned from the closure when it’s called, because
ea8adc8c 252that line doesn’t end in a semicolon; just like in function bodies.
7cac9316
XL
253
254Note that this `let` statement means `expensive_closure` contains the
255*definition* of an anonymous function, not the *resulting value* of calling the
ea8adc8c 256anonymous function. Recall that we’re using a closure because we want to define
ff7c6d11
XL
257the code to call at one point, store that code, and call it at a later point;
258the code we want to call is now stored in `expensive_closure`.
7cac9316 259
ff7c6d11
XL
260With the closure defined, we can change the code in the `if` blocks to call the
261closure to execute the code and get the resulting value. We call a closure like
262we do a function: we specify the variable name that holds the closure
263definition and follow it with parentheses containing the argument values we
264want to use, as shown in Listing 13-6:
cc61c64b 265
7cac9316
XL
266<span class="filename">Filename: src/main.rs</span>
267
268```rust
269# use std::thread;
270# use std::time::Duration;
271#
ea8adc8c 272fn generate_workout(intensity: u32, random_number: u32) {
7cac9316
XL
273 let expensive_closure = |num| {
274 println!("calculating slowly...");
275 thread::sleep(Duration::from_secs(2));
276 num
cc61c64b
XL
277 };
278
7cac9316
XL
279 if intensity < 25 {
280 println!(
281 "Today, do {} pushups!",
282 expensive_closure(intensity)
283 );
284 println!(
285 "Next, do {} situps!",
286 expensive_closure(intensity)
287 );
288 } else {
289 if random_number == 3 {
290 println!("Take a break today! Remember to stay hydrated!");
291 } else {
292 println!(
293 "Today, run for {} minutes!",
294 expensive_closure(intensity)
ea8adc8c 295 );
7cac9316
XL
296 }
297 }
cc61c64b
XL
298}
299```
300
3b2f2976 301<span class="caption">Listing 13-6: Calling the `expensive_closure` we’ve
7cac9316
XL
302defined</span>
303
ea8adc8c
XL
304Now the expensive calculation is called in only one place, and we’re only
305executing that code where we need the results.
306
ff7c6d11
XL
307However, we’ve reintroduced one of the problems from Listing 13-3: we’re still
308calling the closure twice in the first `if` block, which will call the
ea8adc8c
XL
309expensive code twice and make the user wait twice as long as they need to. We
310could fix this problem by creating a variable local to that `if` block to hold
311the result of calling the closure, but closures provide us with another
ff7c6d11
XL
312solution. We’ll talk about that solution in a bit. But first let’s talk about
313why there aren’t type annotations in the closure definition and the traits
314involved with closures.
7cac9316
XL
315
316### Closure Type Inference and Annotation
317
ff7c6d11
XL
318Closures don’t require you to annotate the types of the parameters or the
319return value like `fn` functions do. Type annotations are required on functions
320because they’re part of an explicit interface exposed to your users. Defining
321this interface rigidly is important for ensuring that everyone agrees on what
322types of values a function uses and returns. But closures aren’t used in an
323exposed interface like this: they’re stored in variables and used without
324naming them and exposing them to users of our library.
7cac9316
XL
325
326Additionally, closures are usually short and only relevant within a narrow
327context rather than in any arbitrary scenario. Within these limited contexts,
328the compiler is reliably able to infer the types of the parameters and return
ea8adc8c 329type, similar to how it’s able to infer the types of most variables.
7cac9316 330
ea8adc8c
XL
331Making programmers annotate the types in these small, anonymous functions would
332be annoying and largely redundant with the information the compiler already has
333available.
7cac9316 334
ff7c6d11
XL
335Like variables, we can add type annotations if we want to increase explicitness
336and clarity at the cost of being more verbose than is strictly necessary;
337annotating the types for the closure we defined in Listing 13-4 would look like
338the definition shown in Listing 13-7:
cc61c64b
XL
339
340<span class="filename">Filename: src/main.rs</span>
341
342```rust
7cac9316
XL
343# use std::thread;
344# use std::time::Duration;
345#
ea8adc8c 346let expensive_closure = |num: u32| -> u32 {
7cac9316
XL
347 println!("calculating slowly...");
348 thread::sleep(Duration::from_secs(2));
349 num
350};
cc61c64b
XL
351```
352
7cac9316
XL
353<span class="caption">Listing 13-7: Adding optional type annotations of the
354parameter and return value types in the closure</span>
355
cc61c64b 356The syntax of closures and functions looks more similar with type annotations.
ff7c6d11
XL
357The following is a vertical comparison of the syntax for the definition of a
358function that adds one to its parameter, and a closure that has the same
359behavior. We’ve added some spaces to line up the relevant parts. This
360illustrates how closure syntax is similar to function syntax except for the use
361of pipes and the amount of syntax that is optional:
cc61c64b
XL
362
363```rust,ignore
ea8adc8c
XL
364fn add_one_v1 (x: u32) -> u32 { x + 1 }
365let add_one_v2 = |x: u32| -> u32 { x + 1 };
7cac9316
XL
366let add_one_v3 = |x| { x + 1 };
367let add_one_v4 = |x| x + 1 ;
cc61c64b
XL
368```
369
7cac9316
XL
370The first line shows a function definition, and the second line shows a fully
371annotated closure definition. The third line removes the type annotations from
ea8adc8c 372the closure definition, and the fourth line removes the brackets that are
ff7c6d11 373optional, because the closure body has only one expression. These are all valid
3b2f2976 374definitions that will produce the same behavior when they’re called.
7cac9316 375
7cac9316
XL
376Closure definitions will have one concrete type inferred for each of their
377parameters and for their return value. For instance, Listing 13-8 shows the
ea8adc8c 378definition of a short closure that just returns the value it receives as a
ff7c6d11
XL
379parameter. This closure isn’t very useful except for the purposes of this
380example. Note that we haven’t added any type annotations to the definition: if
381we then try to call the closure twice, using a `String` as an argument the
382first time and a `u32` the second time, we’ll get an error:
cc61c64b
XL
383
384<span class="filename">Filename: src/main.rs</span>
385
386```rust,ignore
7cac9316 387let example_closure = |x| x;
cc61c64b 388
7cac9316
XL
389let s = example_closure(String::from("hello"));
390let n = example_closure(5);
cc61c64b
XL
391```
392
7cac9316
XL
393<span class="caption">Listing 13-8: Attempting to call a closure whose types
394are inferred with two different types</span>
395
cc61c64b
XL
396The compiler gives us this error:
397
398```text
399error[E0308]: mismatched types
7cac9316
XL
400 --> src/main.rs
401 |
402 | let n = example_closure(5);
403 | ^ expected struct `std::string::String`, found
404 integral variable
cc61c64b 405 |
7cac9316
XL
406 = note: expected type `std::string::String`
407 found type `{integer}`
408```
409
7cac9316
XL
410The first time we call `example_closure` with the `String` value, the compiler
411infers the type of `x` and the return type of the closure to be `String`. Those
412types are then locked in to the closure in `example_closure`, and we get a type
413error if we try to use a different type with the same closure.
414
ea8adc8c 415### Storing Closures Using Generic Parameters and the `Fn` Traits
7cac9316 416
ff7c6d11
XL
417Let’s return to our workout generation app. In Listing 13-6, our code was still
418calling the expensive calculation closure more times than it needed to. One
ea8adc8c
XL
419option to solve this issue is to save the result of the expensive closure in a
420variable for reuse and use the variable instead in each place we need the
ff7c6d11 421result instead of calling the closure again. However, this method could result
ea8adc8c
XL
422in a lot of repeated code.
423
ff7c6d11
XL
424Fortunately, another solution is available to us. We can create a struct that
425will hold the closure and the resulting value of calling the closure. The
ea8adc8c 426struct will only execute the closure if we need the resulting value, and it
ff7c6d11 427will cache the resulting value so the rest of our code doesn’t have to be
ea8adc8c
XL
428responsible for saving and reusing the result. You may know this pattern as
429*memoization* or *lazy evaluation*.
7cac9316 430
ff7c6d11
XL
431To make a struct that holds a closure, we need to specify the type of the
432closure, because a struct definition needs to know the types of each of its
433fields. Each closure instance has its own unique anonymous type: that is, even
434if two closures have the same signature, their types are still considered
435different. To define structs, enums, or function parameters that use closures,
436we use generics and trait bounds, as we discussed in Chapter 10.
7cac9316
XL
437
438The `Fn` traits are provided by the standard library. All closures implement
ff7c6d11 439one of the traits: `Fn`, `FnMut`, or `FnOnce`. We’ll discuss the difference
7cac9316
XL
440between these traits in the next section on capturing the environment; in this
441example, we can use the `Fn` trait.
442
443We add types to the `Fn` trait bound to represent the types of the parameters
ff7c6d11
XL
444and return values the closures must have to match this trait bound. In this
445case, our closure has a parameter of type `u32` and returns a `u32`, so the
446trait bound we specify is `Fn(u32) -> u32`.
7cac9316
XL
447
448Listing 13-9 shows the definition of the `Cacher` struct that holds a closure
449and an optional result value:
450
451<span class="filename">Filename: src/main.rs</span>
452
453```rust
454struct Cacher<T>
ea8adc8c 455 where T: Fn(u32) -> u32
7cac9316
XL
456{
457 calculation: T,
ea8adc8c 458 value: Option<u32>,
7cac9316
XL
459}
460```
461
462<span class="caption">Listing 13-9: Defining a `Cacher` struct that holds a
463closure in `calculation` and an optional result in `value`</span>
464
465The `Cacher` struct has a `calculation` field of the generic type `T`. The
ea8adc8c
XL
466trait bounds on `T` specify that it’s a closure by using the `Fn` trait. Any
467closure we want to store in the `calculation` field must have one `u32`
ff7c6d11 468parameter (specified within the parentheses after `Fn`) and must return a
ea8adc8c
XL
469`u32` (specified after the `->`).
470
ff7c6d11
XL
471> Note: Functions implement all three of the `Fn` traits too. If what we want
472> to do doesn’t require capturing a value from the environment, we can use a
ea8adc8c
XL
473> function rather than a closure where we need something that implements an `Fn`
474> trait.
475
476The `value` field is of type `Option<u32>`. Before we execute the closure,
477`value` will be `None`. When code using a `Cacher` asks for the *result* of the
478closure, the `Cacher` will execute the closure at that time and store the
479result within a `Some` variant in the `value` field. Then if the code asks for
480the result of the closure again, instead of executing the closure again, the
481`Cacher` will return the result held in the `Some` variant.
482
483The logic around the `value` field we’ve just described is defined in Listing
48413-10:
7cac9316
XL
485
486<span class="filename">Filename: src/main.rs</span>
487
488```rust
489# struct Cacher<T>
ea8adc8c 490# where T: Fn(u32) -> u32
7cac9316
XL
491# {
492# calculation: T,
ea8adc8c 493# value: Option<u32>,
7cac9316
XL
494# }
495#
496impl<T> Cacher<T>
ea8adc8c 497 where T: Fn(u32) -> u32
7cac9316
XL
498{
499 fn new(calculation: T) -> Cacher<T> {
500 Cacher {
501 calculation,
502 value: None,
503 }
504 }
505
ea8adc8c 506 fn value(&mut self, arg: u32) -> u32 {
7cac9316
XL
507 match self.value {
508 Some(v) => v,
509 None => {
510 let v = (self.calculation)(arg);
511 self.value = Some(v);
512 v
513 },
514 }
515 }
516}
517```
518
ea8adc8c
XL
519<span class="caption">Listing 13-10: The caching logic of `Cacher`</span>
520
ff7c6d11 521We want `Cacher` to manage the struct fields’ values rather than letting the
ea8adc8c
XL
522calling code potentially change the values in these fields directly, so these
523fields are private.
524
525The `Cacher::new` function takes a generic parameter `T`, which we’ve defined
526as having the same trait bound as the `Cacher` struct. Then `Cacher::new`
527returns a `Cacher` instance that holds the closure specified in the
ff7c6d11 528`calculation` field and a `None` value in the `value` field, because we haven’t
ea8adc8c 529executed the closure yet.
7cac9316
XL
530
531When the calling code wants the result of evaluating the closure, instead of
532calling the closure directly, it will call the `value` method. This method
ff7c6d11 533checks whether we already have a resulting value in `self.value` in a `Some`;
7cac9316
XL
534if we do, it returns the value within the `Some` without executing the closure
535again.
536
537If `self.value` is `None`, we call the closure stored in `self.calculation`,
538save the result in `self.value` for future use, and return the value as well.
539
540Listing 13-11 shows how we can use this `Cacher` struct in the
541`generate_workout` function from Listing 13-6:
542
543<span class="filename">Filename: src/main.rs</span>
544
545```rust
546# use std::thread;
547# use std::time::Duration;
548#
549# struct Cacher<T>
ea8adc8c 550# where T: Fn(u32) -> u32
7cac9316
XL
551# {
552# calculation: T,
ea8adc8c 553# value: Option<u32>,
7cac9316
XL
554# }
555#
556# impl<T> Cacher<T>
ea8adc8c 557# where T: Fn(u32) -> u32
7cac9316
XL
558# {
559# fn new(calculation: T) -> Cacher<T> {
560# Cacher {
561# calculation,
562# value: None,
563# }
564# }
565#
ea8adc8c 566# fn value(&mut self, arg: u32) -> u32 {
7cac9316
XL
567# match self.value {
568# Some(v) => v,
569# None => {
570# let v = (self.calculation)(arg);
571# self.value = Some(v);
572# v
573# },
574# }
575# }
576# }
577#
ea8adc8c 578fn generate_workout(intensity: u32, random_number: u32) {
7cac9316
XL
579 let mut expensive_result = Cacher::new(|num| {
580 println!("calculating slowly...");
581 thread::sleep(Duration::from_secs(2));
582 num
583 });
584
585 if intensity < 25 {
586 println!(
587 "Today, do {} pushups!",
588 expensive_result.value(intensity)
589 );
590 println!(
591 "Next, do {} situps!",
592 expensive_result.value(intensity)
593 );
594 } else {
595 if random_number == 3 {
596 println!("Take a break today! Remember to stay hydrated!");
597 } else {
598 println!(
599 "Today, run for {} minutes!",
600 expensive_result.value(intensity)
ea8adc8c 601 );
7cac9316
XL
602 }
603 }
604}
605```
606
607<span class="caption">Listing 13-11: Using `Cacher` in the `generate_workout`
608function to abstract away the caching logic</span>
609
7cac9316
XL
610Instead of saving the closure in a variable directly, we save a new instance of
611`Cacher` that holds the closure. Then, in each place we want the result, we
612call the `value` method on the `Cacher` instance. We can call the `value`
613method as many times as we want, or not call it at all, and the expensive
ea8adc8c
XL
614calculation will be run a maximum of once.
615
616Try running this program with the `main` function from Listing 13-2. Change the
617values in the `simulated_user_specified_value` and `simulated_random_number`
ff7c6d11
XL
618variables to verify that in all the cases in the various `if` and `else`
619blocks, `calculating slowly...` only appears once and only when needed. The
ea8adc8c 620`Cacher` takes care of the logic necessary to ensure we aren’t calling the
ff7c6d11
XL
621expensive calculation more than we need to, so `generate_workout` can focus on
622the business logic.
ea8adc8c
XL
623
624### Limitations of the `Cacher` Implementation
625
626Caching values is a generally useful behavior that we might want to use in
ff7c6d11 627other parts of our code with different closures. However, there are two
ea8adc8c
XL
628problems with the current implementation of `Cacher` that would make reusing it
629in different contexts difficult.
7cac9316 630
ff7c6d11
XL
631The first problem is that a `Cacher` instance assumes it will always get the
632same value for the parameter `arg` to the `value` method. That is, this test of
7cac9316
XL
633`Cacher` will fail:
634
635```rust,ignore
636#[test]
637fn call_with_different_values() {
638 let mut c = Cacher::new(|a| a);
639
640 let v1 = c.value(1);
641 let v2 = c.value(2);
642
643 assert_eq!(v2, 2);
644}
cc61c64b
XL
645```
646
7cac9316 647This test creates a new `Cacher` instance with a closure that returns the value
ea8adc8c
XL
648passed into it. We call the `value` method on this `Cacher` instance with an
649`arg` value of 1 and then an `arg` value of 2, and we expect that the call to
650`value` with the `arg` value of 2 should return 2.
cc61c64b 651
ff7c6d11
XL
652Run this test with the `Cacher` implementation in Listing 13-9 and Listing
65313-10, and the test will fail on the `assert_eq!` with this message:
cc61c64b 654
7cac9316 655```text
ff7c6d11
XL
656thread 'call_with_different_values' panicked at 'assertion failed: `(left == right)`
657 left: `1`,
658 right: `2`', src/main.rs
7cac9316 659```
cc61c64b 660
7cac9316 661The problem is that the first time we called `c.value` with 1, the `Cacher`
ff7c6d11 662instance saved `Some(1)` in `self.value`. Thereafter, no matter what we pass in
ea8adc8c 663to the `value` method, it will always return 1.
7cac9316
XL
664
665Try modifying `Cacher` to hold a hash map rather than a single value. The keys
666of the hash map will be the `arg` values that are passed in, and the values of
667the hash map will be the result of calling the closure on that key. Instead of
668looking at whether `self.value` directly has a `Some` or a `None` value, the
ff7c6d11
XL
669`value` function will look up the `arg` in the hash map and return the value if
670it’s present. If it’s not present, the `Cacher` will call the closure and save
671the resulting value in the hash map associated with its `arg` value.
7cac9316 672
ff7c6d11
XL
673The second problem with the current `Cacher` implementation is that it only
674accepts closures that take one parameter of type `u32` and return a `u32`. We
ea8adc8c
XL
675might want to cache the results of closures that take a string slice and return
676`usize` values, for example. To fix this issue, try introducing more generic
677parameters to increase the flexibility of the `Cacher` functionality.
7cac9316 678
ff7c6d11 679### Capturing the Environment with Closures
7cac9316
XL
680
681In the workout generator example, we only used closures as inline anonymous
ff7c6d11
XL
682functions. However, closures have an additional capability that functions don’t
683have: they can capture their environment and access variables from the scope in
684which they’re defined.
7cac9316
XL
685
686Listing 13-12 has an example of a closure stored in the variable `equal_to_x`
3b2f2976 687that uses the variable `x` from the closure’s surrounding environment:
7cac9316 688
cc61c64b
XL
689<span class="filename">Filename: src/main.rs</span>
690
691```rust
692fn main() {
693 let x = 4;
694
695 let equal_to_x = |z| z == x;
696
697 let y = 4;
698
699 assert!(equal_to_x(y));
700}
701```
702
7cac9316 703<span class="caption">Listing 13-12: Example of a closure that refers to a
cc61c64b
XL
704variable in its enclosing scope</span>
705
706Here, even though `x` is not one of the parameters of `equal_to_x`, the
3b2f2976 707`equal_to_x` closure is allowed to use the `x` variable that’s defined in the
7cac9316
XL
708same scope that `equal_to_x` is defined in.
709
ff7c6d11
XL
710We can’t do the same with functions; if we try with the following example, our
711code won’t compile:
cc61c64b
XL
712
713<span class="filename">Filename: src/main.rs</span>
714
715```rust,ignore
716fn main() {
717 let x = 4;
718
719 fn equal_to_x(z: i32) -> bool { z == x }
720
721 let y = 4;
722
723 assert!(equal_to_x(y));
724}
725```
726
727We get an error:
728
729```text
ff7c6d11
XL
730error[E0434]: can't capture dynamic environment in a fn item; use the || { ...
731} closure form instead
732 --> src/main.rs
cc61c64b
XL
733 |
7344 | fn equal_to_x(z: i32) -> bool { z == x }
735 | ^
736```
737
738The compiler even reminds us that this only works with closures!
739
ea8adc8c
XL
740When a closure captures a value from its environment, it uses memory to store
741the values for use in the closure body. This use of memory is overhead that we
ff7c6d11 742don’t want to pay in more common cases where we want to execute code that
ea8adc8c
XL
743doesn’t capture its environment. Because functions are never allowed to capture
744their environment, defining and using functions will never incur this overhead.
7cac9316
XL
745
746Closures can capture values from their environment in three ways, which
747directly map to the three ways a function can take a parameter: taking
ea8adc8c
XL
748ownership, borrowing immutably, and borrowing mutably. These are encoded in the
749three `Fn` traits as follows:
750
751* `FnOnce` consumes the variables it captures from its enclosing scope, known
ff7c6d11
XL
752 as the closure’s *environment*. To consume the captured variables, the
753 closure must take ownership of these variables and move them into the closure
754 when it is defined. The `Once` part of the name represents the fact that the
ea8adc8c 755 closure can’t take ownership of the same variables more than once, so it can
2c00a5a8 756 be called only once.
7cac9316 757* `Fn` borrows values from the environment immutably.
ff7c6d11 758* `FnMut` can change the environment because it mutably borrows values.
7cac9316 759
ff7c6d11
XL
760When we create a closure, Rust infers which trait to use based on how the
761closure uses the values from the environment. In Listing 13-12, the
762`equal_to_x` closure borrows `x` immutably (so `equal_to_x` has the `Fn` trait)
763because the body of the closure only needs to read the value in `x`.
7cac9316
XL
764
765If we want to force the closure to take ownership of the values it uses in the
ff7c6d11
XL
766environment, we can use the `move` keyword before the parameter list. This
767technique is mostly useful when passing a closure to a new thread to move the
768data so it’s owned by the new thread.
ea8adc8c
XL
769
770We’ll have more examples of `move` closures in Chapter 16 when we talk about
ff7c6d11 771concurrency. For now, here’s the code from Listing 13-12 with the `move`
ea8adc8c 772keyword added to the closure definition and using vectors instead of integers,
ff7c6d11
XL
773because integers can be copied rather than moved; note that this code will not
774yet compile:
cc61c64b 775
7cac9316 776<span class="filename">Filename: src/main.rs</span>
cc61c64b 777
7cac9316
XL
778```rust,ignore
779fn main() {
780 let x = vec![1, 2, 3];
cc61c64b 781
7cac9316 782 let equal_to_x = move |z| z == x;
cc61c64b 783
7cac9316 784 println!("can't use x here: {:?}", x);
cc61c64b 785
7cac9316 786 let y = vec![1, 2, 3];
cc61c64b 787
7cac9316
XL
788 assert!(equal_to_x(y));
789}
cc61c64b
XL
790```
791
ff7c6d11 792We receive the following error:
cc61c64b 793
7cac9316
XL
794```text
795error[E0382]: use of moved value: `x`
796 --> src/main.rs:6:40
797 |
7984 | let equal_to_x = move |z| z == x;
799 | -------- value moved (into closure) here
8005 |
8016 | println!("can't use x here: {:?}", x);
802 | ^ value used here after move
803 |
804 = note: move occurs because `x` has type `std::vec::Vec<i32>`, which does not
ff7c6d11 805 implement the `Copy` trait
7cac9316 806```
cc61c64b 807
ea8adc8c
XL
808The `x` value is moved into the closure when the closure is defined, because we
809added the `move` keyword. The closure then has ownership of `x`, and `main`
810isn’t allowed to use `x` anymore in the `println!` statement. Removing
811`println!` will fix this example.
cc61c64b 812
7cac9316
XL
813Most of the time when specifying one of the `Fn` trait bounds, you can start
814with `Fn` and the compiler will tell you if you need `FnMut` or `FnOnce` based
815on what happens in the closure body.
cc61c64b 816
7cac9316 817To illustrate situations where closures that can capture their environment are
3b2f2976 818useful as function parameters, let’s move on to our next topic: iterators.