]>
Commit | Line | Data |
---|---|---|
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 | |
4 | did more with earlier chapters but we (I, included!) have got a bit lax with. I | |
5 | don't think this is quite right, is there a shorter heading we could use to | |
6 | capture what a closure is/is for? --> | |
7 | <!-- I've attempted a more descriptive subtitle, what do you think? /Carol --> | |
8 | ||
9 | Rust's *closures* are anonymous functions that you can save in a variable or | |
10 | pass as arguments to other functions. You can create the closure in one place, | |
11 | and then call the closure to evaluate it in a different context. Unlike | |
12 | functions, closures are allowed to capture values from the scope in which they | |
13 | are called. We're going to demonstrate how these features of closures allow for | |
14 | code reuse and customization of behavior. | |
15 | ||
16 | <!-- Can you say what sets closures apart from functions, explicitly, above? I | |
17 | can't see it clearly enough to be confident, after one read through this | |
18 | chapter. I think it would help to have the closure definition up front, to help | |
19 | to let the reader know what they are looking out for in the examples. When | |
20 | would you use a closure, for example, rather than using a function? And is it | |
21 | the closure that's stored in the variable, or is it the result of applying the | |
22 | closure to a value passed as an argument? --> | |
23 | <!-- I've tried reworking the above paragraph and restructuring the examples to | |
24 | be more motivating. I've also tried to make it clear throughout that storing a | |
25 | closure is storing the *unevaluated* code, and then you call the closure in | |
26 | order to get the result. /Carol --> | |
27 | ||
28 | ### Creating an Abstraction of Behavior Using a Closure | |
29 | ||
30 | Let's work on an example that will show a situation where storing a closure to | |
31 | be executed at a later time is useful. We'll talk about the syntax of closures, | |
32 | type inference, and traits along the way. | |
33 | ||
34 | The hypothetical situation is this: we're working at a startup that's making an | |
35 | app to generate custom exercise workout plans. The backend is written in Rust, | |
36 | and the algorithm that generates the workout plan takes into account many | |
37 | different factors like the app user's age, their Body Mass Index, their | |
38 | preferences, their recent workouts, and an intensity number they specify. The | |
39 | actual algorithm used isn't important in this example; what's important is that | |
40 | this calculation takes a few seconds. We only want to call this algorithm if we | |
41 | need to, and we only want to call it once, so that we aren't making the user | |
42 | wait more than they need to. We're going to simulate calling this hypothetical | |
43 | algorithm by calling the `simulated_expensive_calculation` function shown in | |
44 | Listing 13-1 instead, which will print `calculating slowly...`, wait for two | |
45 | seconds, and then return whatever number we passed in: | |
cc61c64b | 46 | |
7cac9316 XL |
47 | <span class="filename">Filename: src/main.rs</span> |
48 | ||
49 | ```rust | |
50 | use std::thread; | |
51 | use std::time::Duration; | |
52 | ||
53 | fn 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 | |
61 | hypothetical calculation that takes about two seconds to run</span> | |
62 | ||
63 | Next, we have a `main` function that contains the parts of the workout app that | |
64 | are important for this example. This represents the code that the app would | |
65 | call when a user asks for a workout plan. Because the interaction with the | |
66 | app's frontend isn't relevant to the use of closures, we're going to hardcode | |
67 | values representing inputs to our program and print the outputs. | |
68 | ||
69 | The 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 | ||
76 | The output the program prints will be the recommended workout plan. | |
77 | ||
78 | Listing 13-2 shows the `main` function we're going to use. We've hardcoded the | |
79 | variable `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 | |
81 | get the intensity number from the app frontend and we'd use the `rand` crate to | |
82 | generate a random number like we did in the Guessing Game example in Chapter 2. | |
83 | The `main` function calls a `generate_workout` function with the simulated | |
84 | input values: | |
cc61c64b XL |
85 | |
86 | <span class="filename">Filename: src/main.rs</span> | |
87 | ||
88 | ```rust | |
89 | fn 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 | |
99 | values to simulate user input and random number generation inputs to the | |
100 | `generate_workout` function</span> | |
101 | ||
102 | That's the context of what we're working on. The `generate_workout` function in | |
103 | Listing 13-3 contains the business logic of the app that we're most concerned | |
104 | with in this example. The rest of the code changes in this example will be made | |
105 | to 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 | # | |
119 | fn 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 |
143 | prints the workout plans based on the inputs and calls to the | |
144 | `simulated_expensive_calculation` function</span> | |
145 | ||
146 | The code in Listing 13-3 has multiple calls to the slow calculation function. | |
147 | The first `if` block calls `simulated_expensive_calculation` twice, the `if` | |
148 | inside the outer `else` doesn't call it at all, and the code inside the `else` | |
149 | case inside the outer `else` calls it once. | |
150 | ||
151 | <!-- Will add wingdings in libreoffice /Carol --> | |
152 | ||
153 | The desired behavior of the `generate_workout` function is to first check if | |
154 | the user wants a low intensity workout (indicated by a number less than 25) or | |
155 | a high intensity workout (25 or more). Low intensity workout plans will | |
156 | recommend a number of pushups and situps based on the complex algorithm we're | |
157 | simulating with the `simulated_expensive_calculation` function, which needs the | |
158 | intensity number as an input. | |
159 | ||
160 | If the user wants a high intensity workout, there's some additional logic: if | |
161 | the value of the random number generated by the app happens to be 3, the app | |
162 | will recommend a break and hydration instead. If not, the user will get a high | |
163 | intensity workout of a number of minutes of running that comes from the complex | |
164 | algorithm. | |
165 | ||
166 | The data science team has let us know that there are going to be some changes | |
167 | to the way we have to call the algorithm, so we want to refactor this code to | |
168 | have only one place that calls the `simulated_expensive_calculation` function | |
169 | to update when those changes happen. We also want to get rid of the spot where | |
170 | we're currently calling the function twice unnecessarily, and we don't want to | |
171 | add any other calls to that function in the process. That is, we don't want to | |
172 | call it if we're in the case where the result isn't needed at all, and we still | |
173 | want to call it only once in the last case. | |
174 | ||
175 | There are many ways we could restructure this program. The way we're going to | |
176 | try first is extracting the duplicated call to the expensive calculation | |
177 | function 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 | # | |
191 | fn 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 | |
219 | storing the result in the `expensive_result` variable</span> | |
cc61c64b | 220 | |
7cac9316 XL |
221 | <!-- Will add ghosting and wingdings in libreoffice /Carol --> |
222 | ||
223 | This change unifies all the calls to `simulated_expensive_calculation` and | |
224 | solves the problem of the first `if` block calling the function twice | |
225 | unnecessarily. Unfortunately, we're now calling this function and waiting for | |
226 | the result in all cases, which includes the inner `if` block that doesn't use | |
227 | the result value at all. | |
228 | ||
229 | We want to be able to specify some code in one place in our program, but then | |
230 | only execute that code if we actually need the result in some other place in | |
231 | our program. This is a use case for closures! | |
232 | ||
233 | ### Closures Store Code to be Executed Later | |
234 | ||
235 | Instead of always calling the `simulated_expensive_calculation` function before | |
236 | the `if` blocks, we can define a closure and store the closure in a variable | |
237 | instead of the result as shown in Listing 13-5. We can actually choose to move | |
238 | the whole body of `simulated_expensive_calculation` within the closure we're | |
239 | introducing 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 | # | |
247 | let 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 |
256 | in the expensive function and store the closure in the `expensive_closure` | |
257 | variable</span> | |
258 | ||
259 | <!-- Can you elaborate on *how* to define the closure first? I've had a go here | |
260 | based on what I can see but not sure it's correct. Are we saying that a closure | |
261 | is 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 | |
263 | paragraphs starting here, is this better? /Carol --> | |
264 | ||
265 | The closure definition is the part after the `=` that we're assigning to the | |
266 | variable `expensive_closure`. To define a closure, we start with a pair of | |
267 | vertical pipes (`|`). Inside the pipes is where we specify the parameters to | |
268 | the closure; this syntax was chosen because of its similarity to closure | |
269 | definitions in Smalltalk and Ruby. This closure has one parameter named `num`; | |
270 | if we had more than one parameter, we would separate them with commas, like | |
271 | `|param1, param2|`. | |
272 | ||
273 | After the parameters, we put curly braces that hold the body of the closure. | |
274 | The curly braces are optional if the closure body only has one line. After the | |
275 | curly braces, we need a semicolon to go with the `let` statement. The value | |
276 | returned from the last line in the closure body (`num`), since that line | |
277 | doesn't end in a semicolon, will be the value returned from the closure when | |
278 | it's called, just like in function bodies. | |
279 | ||
280 | Note that this `let` statement means `expensive_closure` contains the | |
281 | *definition* of an anonymous function, not the *resulting value* of calling the | |
282 | anonymous function. Recall the reason we're using a closure is because we want | |
283 | to define the code to call at one point, store that code, and actually call it | |
284 | at a later point; the code we want to call is now stored in `expensive_closure`. | |
285 | ||
286 | Now that we have the closure defined, we can change the code in the `if` blocks | |
287 | to call the closure in order to execute the code and get the resulting value. | |
288 | Calling a closure looks very similar to calling a function; we specify the | |
289 | variable name that holds the closure definition and follow it with parentheses | |
290 | containing the argument values we want to use for that call as shown in Listing | |
291 | 13-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 | # | |
299 | fn 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 |
329 | defined</span> | |
330 | ||
331 | Now we've achieved the goal of unifying where the expensive calculation is | |
332 | called to one place, and we're only executing that code where we need the | |
333 | results. However, we've reintroduced one of the problems from Listing 13-3: | |
334 | we're still calling the closure twice in the first `if` block, which will call | |
335 | the expensive code twice and make the user wait twice as long as they need to. | |
336 | We could fix this problem by creating a variable local to that `if` block to | |
337 | hold the result of calling the closure, but there's another solution we can use | |
338 | since we have a closure. We'll get back to that solution in a bit; let's first | |
339 | talk about why there aren't type annotations in the closure definition and the | |
340 | traits involved with closures. | |
341 | ||
342 | ### Closure Type Inference and Annotation | |
343 | ||
344 | Closure are different than functions defined with the `fn` keyword in a few | |
345 | ways. The first is that closures don't require you to annotate the types of the | |
346 | parameters 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 |
349 | section difficult to follow with this next paragraph --> | |
cc61c64b | 350 | |
7cac9316 XL |
351 | Type annotations are required on functions because they're are part of an |
352 | explicit interface exposed to your users. Defining this interface rigidly is | |
353 | important for ensuring that everyone agrees on what types of values a function | |
354 | uses and returns. Closures aren't used in an exposed interface like this, | |
355 | though: they're stored in variables and used without naming them and exposing | |
356 | them to be invoked by users of our library. | |
357 | ||
358 | Additionally, closures are usually short and only relevant within a narrow | |
359 | context rather than in any arbitrary scenario. Within these limited contexts, | |
360 | the compiler is reliably able to infer the types of the parameters and return | |
361 | type similarly to how it's able to infer the types of most variables. Being | |
362 | forced to annotate the types in these small, anonymous functions would be | |
363 | annoying and largely redundant with the information the compiler already has | |
364 | available. | |
365 | ||
366 | <!--Can you expand above on what you mean by "stored in bindings and called | |
367 | directly"? Do you mean stored in a variable? I'm struggling to visualize how | |
368 | closures are used, and what the important difference is between them and | |
369 | functions. I think a clearer definition of what they are, what they do, and | |
370 | what they're used for at the start of the closures section would help clear | |
371 | this 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 | |
374 | and more like what people are used to by referring to the concept as "variable" | |
375 | throughout, but we missed this spot. /Carol --> | |
376 | ||
377 | Like variables, we can choose to add type annotations if we want to increase | |
378 | explicitness and clarity in exchange for being more verbose than is strictly | |
379 | necessary; annotating the types for the closure we defined in Listing 13-4 | |
380 | would 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 | # | |
388 | let 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 |
396 | parameter 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 | |
402 | thing as the functions? --> | |
403 | <!-- Yes /Carol --> | |
cc61c64b XL |
404 | |
405 | The syntax of closures and functions looks more similar with type annotations. | |
7cac9316 XL |
406 | Here's a vertical comparison of the syntax for the definition of a function |
407 | that adds one to its parameter, and a closure that has the same behavior. We've | |
408 | added some spaces here to line up the relevant parts). This illustrates how | |
409 | closure syntax is similar to function syntax except for the use of pipes rather | |
410 | than 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 | |
414 | of our other code examples as shown in the text? That's concerning to me, we're | |
415 | trying to illustrate idiomatic Rust style in all the code examples, so our | |
416 | alignment is always intentional... /Carol --> | |
cc61c64b XL |
417 | |
418 | ```rust,ignore | |
7cac9316 XL |
419 | fn add_one_v1 (x: i32) -> i32 { x + 1 } |
420 | let add_one_v2 = |x: i32| -> i32 { x + 1 }; | |
421 | let add_one_v3 = |x| { x + 1 }; | |
422 | let 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 |
426 | differences lie? --> | |
427 | <!-- The importance isn't the difference but the similarity... I've tried to | |
428 | clarify /Carol --> | |
429 | ||
430 | <!-- Will add wingdings and ghosting in libreoffice /Carol --> | |
431 | ||
432 | The first line shows a function definition, and the second line shows a fully | |
433 | annotated closure definition. The third line removes the type annotations from | |
434 | the closure definition, and the fourth line removes the braces that are | |
435 | optional since the closure body only has one line. These are all valid | |
436 | definitions 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 | |
439 | like we're annotating it. --> | |
440 | <!-- The types in the function definitions are being inferred, but since Rust's | |
441 | variable types for numbers defaults to `i32`, in order to illustrate our point | |
442 | here we're forcing the type of the *variable* to be `i8` by annotating it in | |
443 | the *variable* declaration. I've changed the example to hopefully be less | |
444 | confusing and convey our point better. /Carol --> | |
445 | ||
446 | Closure definitions will have one concrete type inferred for each of their | |
447 | parameters and for their return value. For instance, Listing 13-8 shows the | |
448 | definition of a short closure that just returns the value it gets as a | |
449 | parameter. This closure isn't very useful except for the purposes of this | |
450 | example. Note that we haven't added any type annotations to the definition: if | |
451 | we then try to call the closure twice, using a `String` as an argument the | |
452 | first 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 | 457 | let example_closure = |x| x; |
cc61c64b | 458 | |
7cac9316 XL |
459 | let s = example_closure(String::from("hello")); |
460 | let n = example_closure(5); | |
cc61c64b XL |
461 | ``` |
462 | ||
7cac9316 XL |
463 | <span class="caption">Listing 13-8: Attempting to call a closure whose types |
464 | are inferred with two different types</span> | |
465 | ||
cc61c64b XL |
466 | The compiler gives us this error: |
467 | ||
468 | ```text | |
469 | error[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 | ||
482 | The first time we call `example_closure` with the `String` value, the compiler | |
483 | infers the type of `x` and the return type of the closure to be `String`. Those | |
484 | types are then locked in to the closure in `example_closure`, and we get a type | |
485 | error if we try to use a different type with the same closure. | |
486 | ||
487 | ### Using Closures with Generic Parameters and the `Fn` Traits | |
488 | ||
489 | Returning to our workout generation app, in Listing 13-6 we left our code still | |
490 | calling the expensive calculation closure more times than it needs to. In each | |
491 | place throughout our code, if we need the results of the expensive closure more | |
492 | than once, we could save the result in a variable for reuse and use the | |
493 | variable instead of calling the closure again. This could be a lot of repeated | |
494 | code saving the results in a variety of places. | |
495 | ||
496 | However, because we have a closure for the expensive calculation, we have | |
497 | another solution available to us. We can create a struct that will hold the | |
498 | closure and the resulting value of calling the closure. The struct will only | |
499 | execute the closure if we need the resulting value, and it will cache the | |
500 | resulting value so that the rest of our code doesn't have to be responsible for | |
501 | saving and reusing the result. You may know this pattern as *memoization* or | |
502 | *lazy evaluation*. | |
503 | ||
504 | In order to make a struct that holds a closure, we need to be able to specify | |
505 | the type of the closure. Each closure instance has its own unique anonymous | |
506 | type: that is, even if two closures have the same signature, their types are | |
507 | still considered to be different. In order to define structs, enums, or | |
508 | function parameters that use closures, we use generics and trait bounds like we | |
509 | discussed in Chapter 10. | |
510 | ||
511 | <!-- So Fn is a trait built into the language, is that right? I wasn't sure if | |
512 | it was just a placeholder here --> | |
513 | <!-- Fn is provided by the standard library; I've clarified here. /Carol --> | |
514 | ||
515 | The `Fn` traits are provided by the standard library. All closures implement | |
516 | one of the traits `Fn`, `FnMut`, or `FnOnce`. We'll discuss the difference | |
517 | between these traits in the next section on capturing the environment; in this | |
518 | example, we can use the `Fn` trait. | |
519 | ||
520 | We add types to the `Fn` trait bound to represent the types of the parameters | |
521 | and return values that the closures must have in order to match this trait | |
522 | bound. 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 | ||
525 | Listing 13-9 shows the definition of the `Cacher` struct that holds a closure | |
526 | and an optional result value: | |
527 | ||
528 | <span class="filename">Filename: src/main.rs</span> | |
529 | ||
530 | ```rust | |
531 | struct 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 | |
540 | closure in `calculation` and an optional result in `value`</span> | |
541 | ||
542 | The `Cacher` struct has a `calculation` field of the generic type `T`. The | |
543 | trait bounds on `T` specify that `T` is a closure by using the `Fn` trait. Any | |
544 | closure we want to store in the `calculation` field of a `Cacher` instance must | |
545 | have one `i32` parameter (specified within the parentheses after `Fn`) and must | |
546 | return an `i32` (specified after the `->`). | |
547 | ||
548 | The `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 | |
550 | closure, 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 | |
552 | the closure again, instead of executing the closure again, we'll return the | |
553 | result that we're holding in the `Some` variant. | |
554 | ||
555 | The logic around the `value` field that we've just described is defined in | |
556 | Listing 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 | # | |
568 | impl<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 | |
592 | saying `calculation` instead of `calculation: calculation`, since the parameter | |
593 | matches the struct field name. This was recently added to stable Rust and we've | |
594 | added an introduction of it in Chapter 5. Just adding an explanation here for | |
595 | you 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 | |
599 | associated function named `new` and a method named `value` that manage the | |
600 | caching logic</span> | |
601 | ||
602 | The fields on the `Cacher` struct are private since we want `Cacher` to manage | |
603 | their values rather than letting the calling code potentially change the values | |
604 | in 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 | |
606 | trait bound as the `Cacher` struct. `Cacher::new` returns a `Cacher` instance | |
607 | that holds the closure specified in the `calculation` field and a `None` value | |
608 | in the `value` field, since we haven't executed the closure yet. | |
609 | ||
610 | When the calling code wants the result of evaluating the closure, instead of | |
611 | calling the closure directly, it will call the `value` method. This method | |
612 | checks to see if we already have a resulting value in `self.value` in a `Some`; | |
613 | if we do, it returns the value within the `Some` without executing the closure | |
614 | again. | |
615 | ||
616 | If `self.value` is `None`, we call the closure stored in `self.calculation`, | |
617 | save the result in `self.value` for future use, and return the value as well. | |
618 | ||
619 | Listing 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 | # | |
657 | fn 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` | |
687 | function to abstract away the caching logic</span> | |
688 | ||
689 | <!-- Will add ghosting and wingdings in libreoffice /Carol --> | |
690 | ||
691 | Instead 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 | |
693 | call the `value` method on the `Cacher` instance. We can call the `value` | |
694 | method as many times as we want, or not call it at all, and the expensive | |
695 | calculation 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 | |
698 | verify 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 | |
700 | needed. | |
701 | ||
702 | The `Cacher` takes care of the logic necessary to ensure we aren't calling the | |
703 | expensive calculation more than we need to be so that `generate_workout` can | |
704 | focus on the business logic. Caching values is a more generally useful behavior | |
705 | that we might want to use in other parts of our code with other closures as | |
706 | well. However, there are a few problems with the current implementation of | |
707 | `Cacher` that would make reusing it in different contexts difficult. | |
708 | ||
709 | The first problem is a `Cacher` instance assumes it will always get the same | |
710 | value for the parameter `arg` to the `value` method. That is, this test of | |
711 | `Cacher` will fail: | |
712 | ||
713 | ```rust,ignore | |
714 | #[test] | |
715 | fn 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 |
725 | This test creates a new `Cacher` instance with a closure that returns the value |
726 | passed into it. We call the `value` method on this `Cacher` instance with | |
727 | an `arg` value of 1 and then an `arg` value of 2, and we expect that the call | |
728 | to `value` with the `arg` value of 2 returns 2. | |
cc61c64b | 729 | |
7cac9316 XL |
730 | Run this with the `Cacher` implementation from Listing 13-9 and Listing 13-10 |
731 | and the test will fail on the `assert_eq!` with this message: | |
cc61c64b | 732 | |
7cac9316 XL |
733 | ```text |
734 | thread 'call_with_different_arg_values' panicked at 'assertion failed: | |
735 | `(left == right)` (left: `1`, right: `2`)', src/main.rs | |
736 | ``` | |
cc61c64b | 737 | |
7cac9316 XL |
738 | The problem is that the first time we called `c.value` with 1, the `Cacher` |
739 | instance saved `Some(1)` in `self.value`. After that, no matter what we pass | |
740 | in to the `value` method, it will always return 1. | |
741 | ||
742 | Try modifying `Cacher` to hold a hash map rather than a single value. The keys | |
743 | of the hash map will be the `arg` values that are passed in, and the values of | |
744 | the hash map will be the result of calling the closure on that key. Instead of | |
745 | looking 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 | |
747 | it's present. If it's not present, the `Cacher` will call the closure and save | |
748 | the resulting value in the hash map associated with its `arg` value. | |
749 | ||
750 | Another problem with the current `Cacher` implementation that restricts its use | |
751 | is that it only accepts closures that take one parameter of type `i32` and | |
752 | return an `i32`. We might want to be able to cache the results of closures that | |
753 | take a string slice as an argument and return `usize` values, for example. Try | |
754 | introducing more generic parameters to increase the flexibility of the `Cacher` | |
755 | functionality. | |
756 | ||
757 | ### Closures Can Capture Their Environment | |
758 | ||
759 | In the workout generator example, we only used closures as inline anonymous | |
760 | functions. Closures have an additional ability we can use that functions don't | |
761 | have, however: they can capture their environment and access variables from the | |
762 | scope in which they're defined. | |
763 | ||
764 | <!-- To clarify, by enclosing scope, do you mean the scope that the closure is | |
765 | inside? Can you expand on that?--> | |
766 | <!-- Yes, I've tried here to clarify that it's the scope in which the closure | |
767 | is defined /Carol --> | |
768 | ||
769 | Listing 13-12 has an example of a closure stored in the variable `equal_to_x` | |
770 | that 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 | |
773 | variable name, or are we referring to the closure as the functionality that is | |
774 | on the right side of the = and so not including to variable name? I thought it | |
775 | was 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; | |
778 | storing a closure in a variable is optional. It should always be the latter, | |
779 | and I hope the reworking of the example used throughout the closure section | |
780 | and 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 | |
786 | fn 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 |
798 | variable in its enclosing scope</span> |
799 | ||
800 | Here, 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 |
802 | same scope that `equal_to_x` is defined in. | |
803 | ||
804 | <!-- So *why* is this allowed with closures and not functions, what about | |
805 | closures makes this safe? --> | |
806 | <!-- It's not really about safety; capturing the environment is the defining | |
807 | functionality a closure has. The reason functions *don't* capture their | |
808 | environment is mostly around storage overhead; I've added an explanation of | |
809 | that aspect. Can you elaborate on what led to the conclusion that allowing | |
810 | captures with functions wouldn't be safe and what you mean by "safe" here? | |
811 | /Carol --> | |
812 | ||
813 | We 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 | |
818 | fn 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 | ||
829 | We get an error: | |
830 | ||
831 | ```text | |
832 | error[E0434]: can't capture dynamic environment in a fn item; use the || { ... } | |
833 | closure form instead | |
834 | --> | |
835 | | | |
836 | 4 | fn equal_to_x(z: i32) -> bool { z == x } | |
837 | | ^ | |
838 | ``` | |
839 | ||
840 | The compiler even reminds us that this only works with closures! | |
841 | ||
7cac9316 XL |
842 | When a closure captures a value from its environment, the closure uses memory |
843 | to store the values for use in the closure body. This use of memory is overhead | |
844 | that we don't want pay for in the more common case where we want to execute | |
845 | code that doesn't capture its environment. Because functions are never allowed | |
846 | to capture their environment, defining and using functions will never incur | |
847 | this overhead. | |
848 | ||
849 | <!-- Why didn't this work, is there a reason ingrained in the language? Or is | |
850 | that not really relevant? --> | |
851 | <!-- I've added an explanation /Carol --> | |
852 | ||
853 | Closures can capture values from their environment in three ways, which | |
854 | directly map to the three ways a function can take a parameter: taking | |
855 | ownership, borrowing immutably, and borrowing mutably. These ways of capturing | |
856 | values 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 | ||
865 | When we create a closure, Rust infers how we want to reference the environment | |
866 | based on how the closure uses the values from the environment. In Listing | |
867 | 13-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 | ||
870 | If we want to force the closure to take ownership of the values it uses in the | |
871 | environment, we can use the `move` keyword before the parameter list. This is | |
872 | mostly useful when passing a closure to a new thread in order to move the data | |
873 | to be owned by the new thread. We'll have more examples of `move` closures in | |
874 | Chapter 16 when we talk about concurrency, but for now here's the code from | |
875 | Listing 13-12 with the `move` keyword added to the closure definition and using | |
876 | vectors 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 |
881 | fn 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 | 894 | This example doesn't compile: |
cc61c64b | 895 | |
7cac9316 XL |
896 | ```text |
897 | error[E0382]: use of moved value: `x` | |
898 | --> src/main.rs:6:40 | |
899 | | | |
900 | 4 | let equal_to_x = move |z| z == x; | |
901 | | -------- value moved (into closure) here | |
902 | 5 | | |
903 | 6 | 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 |
910 | The `x` value is moved into the closure when the closure is defined because of |
911 | the `move` keyword. The closure then has ownership of `x`, and `main` isn't | |
912 | allowed to use `x` anymore. Removing the `println!` will fix this example. | |
cc61c64b | 913 | |
7cac9316 XL |
914 | Most of the time when specifying one of the `Fn` trait bounds, you can start |
915 | with `Fn` and the compiler will tell you if you need `FnMut` or `FnOnce` based | |
916 | on what happens in the closure body. | |
cc61c64b | 917 | |
7cac9316 XL |
918 | To illustrate situations where closures that can capture their environment are |
919 | useful as function parameters, let's move on to our next topic: iterators. |