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