]>
Commit | Line | Data |
---|---|---|
ff7c6d11 | 1 | ## Closures: Anonymous Functions that Can Capture Their Environment |
7cac9316 | 2 | |
ea8adc8c XL |
3 | Rust’s *closures* are anonymous functions you can save in a variable or pass as |
4 | arguments to other functions. You can create the closure in one place, and then | |
5 | call the closure to evaluate it in a different context. Unlike functions, | |
ff7c6d11 XL |
6 | closures can capture values from the scope in which they’re called. We’ll |
7 | demonstrate how these closure features allow for code reuse and behavior | |
8 | customization. | |
7cac9316 | 9 | |
ff7c6d11 | 10 | ### Creating an Abstraction of Behavior with Closures |
7cac9316 | 11 | |
ea8adc8c | 12 | Let’s work on an example of a situation in which it’s useful to store a closure |
ff7c6d11 XL |
13 | to be executed at a later time. Along the way, we’ll talk about the syntax of |
14 | closures, type inference, and traits. | |
ea8adc8c | 15 | |
ff7c6d11 | 16 | Consider this hypothetical situation: we work at a startup that’s making an app |
ea8adc8c XL |
17 | to generate custom exercise workout plans. The backend is written in Rust, and |
18 | the algorithm that generates the workout plan takes into account many different | |
ff7c6d11 | 19 | factors, such as the app user’s age, body mass index, preferences, recent |
ea8adc8c XL |
20 | workouts, and an intensity number they specify. The actual algorithm used isn’t |
21 | important in this example; what’s important is that this calculation takes a | |
ff7c6d11 XL |
22 | few seconds. We want to call this algorithm only when we need to and only call |
23 | it once, so we don’t make the user wait more than necessary. | |
ea8adc8c XL |
24 | |
25 | We’ll simulate calling this hypothetical algorithm with the | |
26 | `simulated_expensive_calculation` function shown in Listing 13-1, which will | |
27 | print `calculating slowly...`, wait for two seconds, and then return whatever | |
28 | number we passed in: | |
cc61c64b | 29 | |
7cac9316 XL |
30 | <span class="filename">Filename: src/main.rs</span> |
31 | ||
32 | ```rust | |
33 | use std::thread; | |
34 | use std::time::Duration; | |
35 | ||
ea8adc8c | 36 | fn 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 |
44 | calculation that takes about two seconds to run</span> | |
7cac9316 | 45 | |
ff7c6d11 XL |
46 | Next is the `main` function that contains the parts of the workout app |
47 | important for this example. This function represents the code that the app will | |
48 | call when a user asks for a workout plan. Because the interaction with the | |
49 | app’s frontend isn’t relevant to the use of closures, we’ll hardcode values | |
ea8adc8c | 50 | representing inputs to our program and print the outputs. |
7cac9316 | 51 | |
ea8adc8c | 52 | The 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 |
59 | The output will be the recommended workout plan. Listing 13-2 shows the `main` |
60 | function we’ll use: | |
cc61c64b XL |
61 | |
62 | <span class="filename">Filename: src/main.rs</span> | |
63 | ||
64 | ```rust | |
65 | fn 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 |
78 | simulate user input and random number generation</span> | |
79 | ||
80 | We’ve hardcoded the variable `simulated_user_specified_value` to 10 and the | |
81 | variable `simulated_random_number` to 7 for simplicity’s sake; in an actual | |
ff7c6d11 XL |
82 | program, 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 |
84 | example in Chapter 2. The `main` function calls a `generate_workout` function |
85 | with the simulated input values. | |
7cac9316 | 86 | |
ff7c6d11 XL |
87 | Now 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 | |
89 | app that we’re most concerned with in this example. The rest of the code | |
90 | changes 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 | 104 | fn 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 |
128 | plans based on the inputs and calls to the `simulated_expensive_calculation` | |
129 | function</span> | |
7cac9316 XL |
130 | |
131 | The code in Listing 13-3 has multiple calls to the slow calculation function. | |
132 | The first `if` block calls `simulated_expensive_calculation` twice, the `if` | |
ea8adc8c XL |
133 | inside the outer `else` doesn’t call it at all, and the code inside the |
134 | second `else` case calls it once. | |
7cac9316 | 135 | |
ff7c6d11 XL |
136 | <!-- NEXT PARAGRAPH WRAPPED WEIRD INTENTIONALLY SEE #199 --> |
137 | ||
138 | The desired behavior of the `generate_workout` function is to first check | |
139 | whether the user wants a low-intensity workout (indicated by a number less | |
140 | than 25) or a high-intensity workout (a number of 25 or greater). | |
ea8adc8c | 141 | |
ff7c6d11 XL |
142 | Low-intensity workout plans will recommend a number of push-ups and sit-ups |
143 | based on the complex algorithm we’re simulating. | |
7cac9316 | 144 | |
ff7c6d11 | 145 | If the user wants a high-intensity workout, there’s some additional logic: if |
7cac9316 | 146 | the value of the random number generated by the app happens to be 3, the app |
ea8adc8c XL |
147 | will recommend a break and hydration. If not, the user will get a number of |
148 | minutes of running based on the complex algorithm. | |
149 | ||
150 | The data science team has let us know that we’ll have to make some changes to | |
151 | the way we call the algorithm in the future. To simplify the update when those | |
ff7c6d11 XL |
152 | changes happen, we want to refactor this code so it calls the |
153 | `simulated_expensive_calculation` function only once. We also want to cut the | |
154 | place where we’re currently unnecessarily calling the function twice without | |
155 | adding any other calls to that function in the process. That is, we don’t want | |
156 | to 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 |
160 | We could restructure the workout program in many ways. First, we’ll try |
161 | extracting the duplicated call to the `expensive_calculation` function into | |
162 | a 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 | 176 | fn 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 | |
206 | This change unifies all the calls to `simulated_expensive_calculation` and | |
ff7c6d11 XL |
207 | solves the problem of the first `if` block unnecessarily calling the function |
208 | twice. Unfortunately, we’re now calling this function and waiting for the | |
209 | result in all cases, which includes the inner `if` block that doesn’t use the | |
210 | result value at all. | |
7cac9316 | 211 | |
ea8adc8c XL |
212 | We want to define code in one place in our program, but only *execute* that |
213 | code 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 | |
217 | Instead of always calling the `simulated_expensive_calculation` function before | |
ea8adc8c XL |
218 | the `if` blocks, we can define a closure and store the *closure* in a variable |
219 | rather than storing the result, as shown in Listing 13-5. We can actually move | |
3b2f2976 | 220 | the whole body of `simulated_expensive_calculation` within the closure we’re |
7cac9316 | 221 | introducing 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 | # | |
229 | let 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 | ||
240 | The 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 | |
242 | pipes (`|`), inside which we specify the parameters to the closure; this syntax | |
243 | was chosen because of its similarity to closure definitions in Smalltalk and | |
ff7c6d11 | 244 | Ruby. This closure has one parameter named `num`: if we had more than one |
ea8adc8c XL |
245 | parameter, we would separate them with commas, like `|param1, param2|`. |
246 | ||
247 | After the parameters, we place curly brackets that hold the body of the | |
248 | closure—these are optional if the closure body is a single expression. The end | |
249 | of 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 | 252 | that line doesn’t end in a semicolon; just like in function bodies. |
7cac9316 XL |
253 | |
254 | Note that this `let` statement means `expensive_closure` contains the | |
255 | *definition* of an anonymous function, not the *resulting value* of calling the | |
ea8adc8c | 256 | anonymous function. Recall that we’re using a closure because we want to define |
ff7c6d11 XL |
257 | the code to call at one point, store that code, and call it at a later point; |
258 | the code we want to call is now stored in `expensive_closure`. | |
7cac9316 | 259 | |
ff7c6d11 XL |
260 | With the closure defined, we can change the code in the `if` blocks to call the |
261 | closure to execute the code and get the resulting value. We call a closure like | |
262 | we do a function: we specify the variable name that holds the closure | |
263 | definition and follow it with parentheses containing the argument values we | |
264 | want 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 | 272 | fn 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 |
302 | defined</span> |
303 | ||
ea8adc8c XL |
304 | Now the expensive calculation is called in only one place, and we’re only |
305 | executing that code where we need the results. | |
306 | ||
ff7c6d11 XL |
307 | However, we’ve reintroduced one of the problems from Listing 13-3: we’re still |
308 | calling the closure twice in the first `if` block, which will call the | |
ea8adc8c XL |
309 | expensive code twice and make the user wait twice as long as they need to. We |
310 | could fix this problem by creating a variable local to that `if` block to hold | |
311 | the result of calling the closure, but closures provide us with another | |
ff7c6d11 XL |
312 | solution. We’ll talk about that solution in a bit. But first let’s talk about |
313 | why there aren’t type annotations in the closure definition and the traits | |
314 | involved with closures. | |
7cac9316 XL |
315 | |
316 | ### Closure Type Inference and Annotation | |
317 | ||
ff7c6d11 XL |
318 | Closures don’t require you to annotate the types of the parameters or the |
319 | return value like `fn` functions do. Type annotations are required on functions | |
320 | because they’re part of an explicit interface exposed to your users. Defining | |
321 | this interface rigidly is important for ensuring that everyone agrees on what | |
322 | types of values a function uses and returns. But closures aren’t used in an | |
323 | exposed interface like this: they’re stored in variables and used without | |
324 | naming them and exposing them to users of our library. | |
7cac9316 XL |
325 | |
326 | Additionally, closures are usually short and only relevant within a narrow | |
327 | context rather than in any arbitrary scenario. Within these limited contexts, | |
328 | the compiler is reliably able to infer the types of the parameters and return | |
ea8adc8c | 329 | type, similar to how it’s able to infer the types of most variables. |
7cac9316 | 330 | |
ea8adc8c XL |
331 | Making programmers annotate the types in these small, anonymous functions would |
332 | be annoying and largely redundant with the information the compiler already has | |
333 | available. | |
7cac9316 | 334 | |
ff7c6d11 XL |
335 | Like variables, we can add type annotations if we want to increase explicitness |
336 | and clarity at the cost of being more verbose than is strictly necessary; | |
337 | annotating the types for the closure we defined in Listing 13-4 would look like | |
338 | the 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 | 346 | let 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 |
354 | parameter and return value types in the closure</span> | |
355 | ||
cc61c64b | 356 | The syntax of closures and functions looks more similar with type annotations. |
ff7c6d11 XL |
357 | The following is a vertical comparison of the syntax for the definition of a |
358 | function that adds one to its parameter, and a closure that has the same | |
359 | behavior. We’ve added some spaces to line up the relevant parts. This | |
360 | illustrates how closure syntax is similar to function syntax except for the use | |
361 | of pipes and the amount of syntax that is optional: | |
cc61c64b XL |
362 | |
363 | ```rust,ignore | |
ea8adc8c XL |
364 | fn add_one_v1 (x: u32) -> u32 { x + 1 } |
365 | let add_one_v2 = |x: u32| -> u32 { x + 1 }; | |
7cac9316 XL |
366 | let add_one_v3 = |x| { x + 1 }; |
367 | let add_one_v4 = |x| x + 1 ; | |
cc61c64b XL |
368 | ``` |
369 | ||
7cac9316 XL |
370 | The first line shows a function definition, and the second line shows a fully |
371 | annotated closure definition. The third line removes the type annotations from | |
ea8adc8c | 372 | the closure definition, and the fourth line removes the brackets that are |
ff7c6d11 | 373 | optional, because the closure body has only one expression. These are all valid |
3b2f2976 | 374 | definitions that will produce the same behavior when they’re called. |
7cac9316 | 375 | |
7cac9316 XL |
376 | Closure definitions will have one concrete type inferred for each of their |
377 | parameters and for their return value. For instance, Listing 13-8 shows the | |
ea8adc8c | 378 | definition of a short closure that just returns the value it receives as a |
ff7c6d11 XL |
379 | parameter. This closure isn’t very useful except for the purposes of this |
380 | example. Note that we haven’t added any type annotations to the definition: if | |
381 | we then try to call the closure twice, using a `String` as an argument the | |
382 | first 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 | 387 | let example_closure = |x| x; |
cc61c64b | 388 | |
7cac9316 XL |
389 | let s = example_closure(String::from("hello")); |
390 | let n = example_closure(5); | |
cc61c64b XL |
391 | ``` |
392 | ||
7cac9316 XL |
393 | <span class="caption">Listing 13-8: Attempting to call a closure whose types |
394 | are inferred with two different types</span> | |
395 | ||
cc61c64b XL |
396 | The compiler gives us this error: |
397 | ||
398 | ```text | |
399 | error[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 |
410 | The first time we call `example_closure` with the `String` value, the compiler |
411 | infers the type of `x` and the return type of the closure to be `String`. Those | |
412 | types are then locked in to the closure in `example_closure`, and we get a type | |
413 | error 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 |
417 | Let’s return to our workout generation app. In Listing 13-6, our code was still |
418 | calling the expensive calculation closure more times than it needed to. One | |
ea8adc8c XL |
419 | option to solve this issue is to save the result of the expensive closure in a |
420 | variable for reuse and use the variable instead in each place we need the | |
ff7c6d11 | 421 | result instead of calling the closure again. However, this method could result |
ea8adc8c XL |
422 | in a lot of repeated code. |
423 | ||
ff7c6d11 XL |
424 | Fortunately, another solution is available to us. We can create a struct that |
425 | will hold the closure and the resulting value of calling the closure. The | |
ea8adc8c | 426 | struct will only execute the closure if we need the resulting value, and it |
ff7c6d11 | 427 | will cache the resulting value so the rest of our code doesn’t have to be |
ea8adc8c XL |
428 | responsible for saving and reusing the result. You may know this pattern as |
429 | *memoization* or *lazy evaluation*. | |
7cac9316 | 430 | |
ff7c6d11 XL |
431 | To make a struct that holds a closure, we need to specify the type of the |
432 | closure, because a struct definition needs to know the types of each of its | |
433 | fields. Each closure instance has its own unique anonymous type: that is, even | |
434 | if two closures have the same signature, their types are still considered | |
435 | different. To define structs, enums, or function parameters that use closures, | |
436 | we use generics and trait bounds, as we discussed in Chapter 10. | |
7cac9316 XL |
437 | |
438 | The `Fn` traits are provided by the standard library. All closures implement | |
ff7c6d11 | 439 | one of the traits: `Fn`, `FnMut`, or `FnOnce`. We’ll discuss the difference |
7cac9316 XL |
440 | between these traits in the next section on capturing the environment; in this |
441 | example, we can use the `Fn` trait. | |
442 | ||
443 | We add types to the `Fn` trait bound to represent the types of the parameters | |
ff7c6d11 XL |
444 | and return values the closures must have to match this trait bound. In this |
445 | case, our closure has a parameter of type `u32` and returns a `u32`, so the | |
446 | trait bound we specify is `Fn(u32) -> u32`. | |
7cac9316 XL |
447 | |
448 | Listing 13-9 shows the definition of the `Cacher` struct that holds a closure | |
449 | and an optional result value: | |
450 | ||
451 | <span class="filename">Filename: src/main.rs</span> | |
452 | ||
453 | ```rust | |
454 | struct 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 | |
463 | closure in `calculation` and an optional result in `value`</span> | |
464 | ||
465 | The `Cacher` struct has a `calculation` field of the generic type `T`. The | |
ea8adc8c XL |
466 | trait bounds on `T` specify that it’s a closure by using the `Fn` trait. Any |
467 | closure we want to store in the `calculation` field must have one `u32` | |
ff7c6d11 | 468 | parameter (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 | ||
476 | The `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 | |
478 | closure, the `Cacher` will execute the closure at that time and store the | |
479 | result within a `Some` variant in the `value` field. Then if the code asks for | |
480 | the result of the closure again, instead of executing the closure again, the | |
481 | `Cacher` will return the result held in the `Some` variant. | |
482 | ||
483 | The logic around the `value` field we’ve just described is defined in Listing | |
484 | 13-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 | # | |
496 | impl<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 | 521 | We want `Cacher` to manage the struct fields’ values rather than letting the |
ea8adc8c XL |
522 | calling code potentially change the values in these fields directly, so these |
523 | fields are private. | |
524 | ||
525 | The `Cacher::new` function takes a generic parameter `T`, which we’ve defined | |
526 | as having the same trait bound as the `Cacher` struct. Then `Cacher::new` | |
527 | returns 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 | 529 | executed the closure yet. |
7cac9316 XL |
530 | |
531 | When the calling code wants the result of evaluating the closure, instead of | |
532 | calling the closure directly, it will call the `value` method. This method | |
ff7c6d11 | 533 | checks whether we already have a resulting value in `self.value` in a `Some`; |
7cac9316 XL |
534 | if we do, it returns the value within the `Some` without executing the closure |
535 | again. | |
536 | ||
537 | If `self.value` is `None`, we call the closure stored in `self.calculation`, | |
538 | save the result in `self.value` for future use, and return the value as well. | |
539 | ||
540 | Listing 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 | 578 | fn 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` | |
608 | function to abstract away the caching logic</span> | |
609 | ||
7cac9316 XL |
610 | Instead 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 | |
612 | call the `value` method on the `Cacher` instance. We can call the `value` | |
613 | method as many times as we want, or not call it at all, and the expensive | |
ea8adc8c XL |
614 | calculation will be run a maximum of once. |
615 | ||
616 | Try running this program with the `main` function from Listing 13-2. Change the | |
617 | values in the `simulated_user_specified_value` and `simulated_random_number` | |
ff7c6d11 XL |
618 | variables to verify that in all the cases in the various `if` and `else` |
619 | blocks, `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 |
621 | expensive calculation more than we need to, so `generate_workout` can focus on |
622 | the business logic. | |
ea8adc8c XL |
623 | |
624 | ### Limitations of the `Cacher` Implementation | |
625 | ||
626 | Caching values is a generally useful behavior that we might want to use in | |
ff7c6d11 | 627 | other parts of our code with different closures. However, there are two |
ea8adc8c XL |
628 | problems with the current implementation of `Cacher` that would make reusing it |
629 | in different contexts difficult. | |
7cac9316 | 630 | |
ff7c6d11 XL |
631 | The first problem is that a `Cacher` instance assumes it will always get the |
632 | same 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] | |
637 | fn 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 | 647 | This test creates a new `Cacher` instance with a closure that returns the value |
ea8adc8c XL |
648 | passed 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 |
652 | Run this test with the `Cacher` implementation in Listing 13-9 and Listing |
653 | 13-10, and the test will fail on the `assert_eq!` with this message: | |
cc61c64b | 654 | |
7cac9316 | 655 | ```text |
ff7c6d11 XL |
656 | thread 'call_with_different_values' panicked at 'assertion failed: `(left == right)` |
657 | left: `1`, | |
658 | right: `2`', src/main.rs | |
7cac9316 | 659 | ``` |
cc61c64b | 660 | |
7cac9316 | 661 | The problem is that the first time we called `c.value` with 1, the `Cacher` |
ff7c6d11 | 662 | instance saved `Some(1)` in `self.value`. Thereafter, no matter what we pass in |
ea8adc8c | 663 | to the `value` method, it will always return 1. |
7cac9316 XL |
664 | |
665 | Try modifying `Cacher` to hold a hash map rather than a single value. The keys | |
666 | of the hash map will be the `arg` values that are passed in, and the values of | |
667 | the hash map will be the result of calling the closure on that key. Instead of | |
668 | looking 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 |
670 | it’s present. If it’s not present, the `Cacher` will call the closure and save | |
671 | the resulting value in the hash map associated with its `arg` value. | |
7cac9316 | 672 | |
ff7c6d11 XL |
673 | The second problem with the current `Cacher` implementation is that it only |
674 | accepts closures that take one parameter of type `u32` and return a `u32`. We | |
ea8adc8c XL |
675 | might 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 | |
677 | parameters to increase the flexibility of the `Cacher` functionality. | |
7cac9316 | 678 | |
ff7c6d11 | 679 | ### Capturing the Environment with Closures |
7cac9316 XL |
680 | |
681 | In the workout generator example, we only used closures as inline anonymous | |
ff7c6d11 XL |
682 | functions. However, closures have an additional capability that functions don’t |
683 | have: they can capture their environment and access variables from the scope in | |
684 | which they’re defined. | |
7cac9316 XL |
685 | |
686 | Listing 13-12 has an example of a closure stored in the variable `equal_to_x` | |
3b2f2976 | 687 | that 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 | |
692 | fn 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 |
704 | variable in its enclosing scope</span> |
705 | ||
706 | Here, 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 |
708 | same scope that `equal_to_x` is defined in. |
709 | ||
ff7c6d11 XL |
710 | We can’t do the same with functions; if we try with the following example, our |
711 | code won’t compile: | |
cc61c64b XL |
712 | |
713 | <span class="filename">Filename: src/main.rs</span> | |
714 | ||
715 | ```rust,ignore | |
716 | fn 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 | ||
727 | We get an error: | |
728 | ||
729 | ```text | |
ff7c6d11 XL |
730 | error[E0434]: can't capture dynamic environment in a fn item; use the || { ... |
731 | } closure form instead | |
732 | --> src/main.rs | |
cc61c64b XL |
733 | | |
734 | 4 | fn equal_to_x(z: i32) -> bool { z == x } | |
735 | | ^ | |
736 | ``` | |
737 | ||
738 | The compiler even reminds us that this only works with closures! | |
739 | ||
ea8adc8c XL |
740 | When a closure captures a value from its environment, it uses memory to store |
741 | the values for use in the closure body. This use of memory is overhead that we | |
ff7c6d11 | 742 | don’t want to pay in more common cases where we want to execute code that |
ea8adc8c XL |
743 | doesn’t capture its environment. Because functions are never allowed to capture |
744 | their environment, defining and using functions will never incur this overhead. | |
7cac9316 XL |
745 | |
746 | Closures can capture values from their environment in three ways, which | |
747 | directly map to the three ways a function can take a parameter: taking | |
ea8adc8c XL |
748 | ownership, borrowing immutably, and borrowing mutably. These are encoded in the |
749 | three `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 |
760 | When we create a closure, Rust infers which trait to use based on how the |
761 | closure 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) | |
763 | because the body of the closure only needs to read the value in `x`. | |
7cac9316 XL |
764 | |
765 | If we want to force the closure to take ownership of the values it uses in the | |
ff7c6d11 XL |
766 | environment, we can use the `move` keyword before the parameter list. This |
767 | technique is mostly useful when passing a closure to a new thread to move the | |
768 | data so it’s owned by the new thread. | |
ea8adc8c XL |
769 | |
770 | We’ll have more examples of `move` closures in Chapter 16 when we talk about | |
ff7c6d11 | 771 | concurrency. For now, here’s the code from Listing 13-12 with the `move` |
ea8adc8c | 772 | keyword added to the closure definition and using vectors instead of integers, |
ff7c6d11 XL |
773 | because integers can be copied rather than moved; note that this code will not |
774 | yet compile: | |
cc61c64b | 775 | |
7cac9316 | 776 | <span class="filename">Filename: src/main.rs</span> |
cc61c64b | 777 | |
7cac9316 XL |
778 | ```rust,ignore |
779 | fn 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 | 792 | We receive the following error: |
cc61c64b | 793 | |
7cac9316 XL |
794 | ```text |
795 | error[E0382]: use of moved value: `x` | |
796 | --> src/main.rs:6:40 | |
797 | | | |
798 | 4 | let equal_to_x = move |z| z == x; | |
799 | | -------- value moved (into closure) here | |
800 | 5 | | |
801 | 6 | 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 |
808 | The `x` value is moved into the closure when the closure is defined, because we |
809 | added the `move` keyword. The closure then has ownership of `x`, and `main` | |
810 | isn’t allowed to use `x` anymore in the `println!` statement. Removing | |
811 | `println!` will fix this example. | |
cc61c64b | 812 | |
7cac9316 XL |
813 | Most of the time when specifying one of the `Fn` trait bounds, you can start |
814 | with `Fn` and the compiler will tell you if you need `FnMut` or `FnOnce` based | |
815 | on what happens in the closure body. | |
cc61c64b | 816 | |
7cac9316 | 817 | To illustrate situations where closures that can capture their environment are |
3b2f2976 | 818 | useful as function parameters, let’s move on to our next topic: iterators. |