]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | % Closures |
2 | ||
b039eaaf SL |
3 | Sometimes it is useful to wrap up a function and _free variables_ for better |
4 | clarity and reuse. The free variables that can be used come from the | |
5 | enclosing scope and are ‘closed over’ when used in the function. From this, we | |
6 | get the name ‘closures’ and Rust provides a really great implementation of | |
7 | them, as we’ll see. | |
1a4d82fc | 8 | |
c34b1796 | 9 | # Syntax |
1a4d82fc | 10 | |
c34b1796 | 11 | Closures look like this: |
1a4d82fc | 12 | |
c34b1796 AL |
13 | ```rust |
14 | let plus_one = |x: i32| x + 1; | |
15 | ||
16 | assert_eq!(2, plus_one(1)); | |
17 | ``` | |
18 | ||
bd371182 | 19 | We create a binding, `plus_one`, and assign it to a closure. The closure’s |
c34b1796 AL |
20 | arguments go between the pipes (`|`), and the body is an expression, in this |
21 | case, `x + 1`. Remember that `{ }` is an expression, so we can have multi-line | |
22 | closures too: | |
23 | ||
24 | ```rust | |
25 | let plus_two = |x| { | |
26 | let mut result: i32 = x; | |
27 | ||
28 | result += 1; | |
29 | result += 1; | |
30 | ||
31 | result | |
32 | }; | |
33 | ||
34 | assert_eq!(4, plus_two(2)); | |
35 | ``` | |
36 | ||
62682a34 | 37 | You’ll notice a few things about closures that are a bit different from regular |
b039eaaf | 38 | named functions defined with `fn`. The first is that we did not need to |
c34b1796 AL |
39 | annotate the types of arguments the closure takes or the values it returns. We |
40 | can: | |
41 | ||
42 | ```rust | |
43 | let plus_one = |x: i32| -> i32 { x + 1 }; | |
44 | ||
45 | assert_eq!(2, plus_one(1)); | |
46 | ``` | |
47 | ||
b039eaaf SL |
48 | But we don’t have to. Why is this? Basically, it was chosen for ergonomic |
49 | reasons. While specifying the full type for named functions is helpful with | |
50 | things like documentation and type inference, the full type signatures of | |
51 | closures are rarely documented since they’re anonymous, and they don’t cause | |
52 | the kinds of error-at-a-distance problems that inferring named function types | |
53 | can. | |
c34b1796 | 54 | |
b039eaaf SL |
55 | The second is that the syntax is similar, but a bit different. I’ve added |
56 | spaces here for easier comparison: | |
c34b1796 AL |
57 | |
58 | ```rust | |
62682a34 SL |
59 | fn plus_one_v1 (x: i32) -> i32 { x + 1 } |
60 | let plus_one_v2 = |x: i32| -> i32 { x + 1 }; | |
61 | let plus_one_v3 = |x: i32| x + 1 ; | |
1a4d82fc JJ |
62 | ``` |
63 | ||
62682a34 | 64 | Small differences, but they’re similar. |
1a4d82fc | 65 | |
c34b1796 | 66 | # Closures and their environment |
1a4d82fc | 67 | |
b039eaaf SL |
68 | The environment for a closure can include bindings from its enclosing scope in |
69 | addition to parameters and local bindings. It looks like this: | |
c34b1796 AL |
70 | |
71 | ```rust | |
72 | let num = 5; | |
73 | let plus_num = |x: i32| x + num; | |
74 | ||
75 | assert_eq!(10, plus_num(5)); | |
1a4d82fc JJ |
76 | ``` |
77 | ||
c34b1796 AL |
78 | This closure, `plus_num`, refers to a `let` binding in its scope: `num`. More |
79 | specifically, it borrows the binding. If we do something that would conflict | |
80 | with that binding, we get an error. Like this one: | |
81 | ||
82 | ```rust,ignore | |
83 | let mut num = 5; | |
84 | let plus_num = |x: i32| x + num; | |
1a4d82fc | 85 | |
c34b1796 AL |
86 | let y = &mut num; |
87 | ``` | |
1a4d82fc | 88 | |
c34b1796 AL |
89 | Which errors with: |
90 | ||
91 | ```text | |
92 | error: cannot borrow `num` as mutable because it is also borrowed as immutable | |
93 | let y = &mut num; | |
94 | ^~~ | |
95 | note: previous borrow of `num` occurs here due to use in closure; the immutable | |
96 | borrow prevents subsequent moves or mutable borrows of `num` until the borrow | |
97 | ends | |
98 | let plus_num = |x| x + num; | |
99 | ^~~~~~~~~~~ | |
100 | note: previous borrow ends here | |
1a4d82fc | 101 | fn main() { |
c34b1796 AL |
102 | let mut num = 5; |
103 | let plus_num = |x| x + num; | |
62682a34 | 104 | |
c34b1796 AL |
105 | let y = &mut num; |
106 | } | |
107 | ^ | |
108 | ``` | |
109 | ||
bd371182 | 110 | A verbose yet helpful error message! As it says, we can’t take a mutable borrow |
c34b1796 AL |
111 | on `num` because the closure is already borrowing it. If we let the closure go |
112 | out of scope, we can: | |
113 | ||
114 | ```rust | |
115 | let mut num = 5; | |
116 | { | |
117 | let plus_num = |x: i32| x + num; | |
118 | ||
119 | } // plus_num goes out of scope, borrow of num ends | |
1a4d82fc | 120 | |
c34b1796 AL |
121 | let y = &mut num; |
122 | ``` | |
123 | ||
124 | If your closure requires it, however, Rust will take ownership and move | |
62682a34 | 125 | the environment instead. This doesn’t work: |
c34b1796 AL |
126 | |
127 | ```rust,ignore | |
128 | let nums = vec![1, 2, 3]; | |
129 | ||
130 | let takes_nums = || nums; | |
131 | ||
132 | println!("{:?}", nums); | |
133 | ``` | |
134 | ||
62682a34 | 135 | We get this error: |
c34b1796 AL |
136 | |
137 | ```text | |
138 | note: `nums` moved into closure environment here because it has type | |
139 | `[closure(()) -> collections::vec::Vec<i32>]`, which is non-copyable | |
140 | let takes_nums = || nums; | |
62682a34 | 141 | ^~~~~~~ |
c34b1796 AL |
142 | ``` |
143 | ||
144 | `Vec<T>` has ownership over its contents, and therefore, when we refer to it | |
bd371182 | 145 | in our closure, we have to take ownership of `nums`. It’s the same as if we’d |
c34b1796 AL |
146 | passed `nums` to a function that took ownership of it. |
147 | ||
148 | ## `move` closures | |
149 | ||
150 | We can force our closure to take ownership of its environment with the `move` | |
151 | keyword: | |
1a4d82fc | 152 | |
c34b1796 AL |
153 | ```rust |
154 | let num = 5; | |
155 | ||
156 | let owns_num = move |x: i32| x + num; | |
157 | ``` | |
158 | ||
159 | Now, even though the keyword is `move`, the variables follow normal move semantics. | |
160 | In this case, `5` implements `Copy`, and so `owns_num` takes ownership of a copy | |
bd371182 | 161 | of `num`. So what’s the difference? |
c34b1796 AL |
162 | |
163 | ```rust | |
164 | let mut num = 5; | |
165 | ||
62682a34 | 166 | { |
c34b1796 AL |
167 | let mut add_num = |x: i32| num += x; |
168 | ||
169 | add_num(5); | |
1a4d82fc | 170 | } |
c34b1796 AL |
171 | |
172 | assert_eq!(10, num); | |
1a4d82fc JJ |
173 | ``` |
174 | ||
c34b1796 | 175 | So in this case, our closure took a mutable reference to `num`, and then when |
bd371182 | 176 | we called `add_num`, it mutated the underlying value, as we’d expect. We also |
c34b1796 AL |
177 | needed to declare `add_num` as `mut` too, because we’re mutating its |
178 | environment. | |
1a4d82fc | 179 | |
bd371182 | 180 | If we change to a `move` closure, it’s different: |
c34b1796 AL |
181 | |
182 | ```rust | |
183 | let mut num = 5; | |
1a4d82fc | 184 | |
62682a34 | 185 | { |
c34b1796 | 186 | let mut add_num = move |x: i32| num += x; |
1a4d82fc | 187 | |
c34b1796 | 188 | add_num(5); |
1a4d82fc | 189 | } |
c34b1796 AL |
190 | |
191 | assert_eq!(5, num); | |
1a4d82fc JJ |
192 | ``` |
193 | ||
c34b1796 AL |
194 | We only get `5`. Rather than taking a mutable borrow out on our `num`, we took |
195 | ownership of a copy. | |
196 | ||
197 | Another way to think about `move` closures: they give a closure its own stack | |
198 | frame. Without `move`, a closure may be tied to the stack frame that created | |
199 | it, while a `move` closure is self-contained. This means that you cannot | |
200 | generally return a non-`move` closure from a function, for example. | |
201 | ||
b039eaaf SL |
202 | But before we talk about taking and returning closures, we should talk some |
203 | more about the way that closures are implemented. As a systems language, Rust | |
204 | gives you tons of control over what your code does, and closures are no | |
205 | different. | |
1a4d82fc | 206 | |
c34b1796 | 207 | # Closure implementation |
1a4d82fc | 208 | |
bd371182 AL |
209 | Rust’s implementation of closures is a bit different than other languages. They |
210 | are effectively syntax sugar for traits. You’ll want to make sure to have read | |
9cc50fc6 | 211 | the [traits][traits] section before this one, as well as the section on [trait |
9346a6ac | 212 | objects][trait-objects]. |
1a4d82fc | 213 | |
c34b1796 | 214 | [traits]: traits.html |
9346a6ac | 215 | [trait-objects]: trait-objects.html |
1a4d82fc | 216 | |
c34b1796 AL |
217 | Got all that? Good. |
218 | ||
219 | The key to understanding how closures work under the hood is something a bit | |
220 | strange: Using `()` to call a function, like `foo()`, is an overloadable | |
221 | operator. From this, everything else clicks into place. In Rust, we use the | |
222 | trait system to overload operators. Calling functions is no different. We have | |
223 | three separate traits to overload with: | |
224 | ||
225 | ```rust | |
226 | # mod foo { | |
227 | pub trait Fn<Args> : FnMut<Args> { | |
228 | extern "rust-call" fn call(&self, args: Args) -> Self::Output; | |
1a4d82fc JJ |
229 | } |
230 | ||
c34b1796 AL |
231 | pub trait FnMut<Args> : FnOnce<Args> { |
232 | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; | |
233 | } | |
234 | ||
235 | pub trait FnOnce<Args> { | |
236 | type Output; | |
1a4d82fc | 237 | |
c34b1796 | 238 | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; |
1a4d82fc | 239 | } |
c34b1796 | 240 | # } |
1a4d82fc JJ |
241 | ``` |
242 | ||
bd371182 | 243 | You’ll notice a few differences between these traits, but a big one is `self`: |
c34b1796 | 244 | `Fn` takes `&self`, `FnMut` takes `&mut self`, and `FnOnce` takes `self`. This |
bd371182 | 245 | covers all three kinds of `self` via the usual method call syntax. But we’ve |
c34b1796 AL |
246 | split them up into three traits, rather than having a single one. This gives us |
247 | a large amount of control over what kind of closures we can take. | |
1a4d82fc | 248 | |
c34b1796 AL |
249 | The `|| {}` syntax for closures is sugar for these three traits. Rust will |
250 | generate a struct for the environment, `impl` the appropriate trait, and then | |
251 | use it. | |
252 | ||
253 | # Taking closures as arguments | |
254 | ||
255 | Now that we know that closures are traits, we already know how to accept and | |
9cc50fc6 | 256 | return closures: the same as any other trait! |
c34b1796 AL |
257 | |
258 | This also means that we can choose static vs dynamic dispatch as well. First, | |
bd371182 | 259 | let’s write a function which takes something callable, calls it, and returns |
c34b1796 AL |
260 | the result: |
261 | ||
262 | ```rust | |
263 | fn call_with_one<F>(some_closure: F) -> i32 | |
264 | where F : Fn(i32) -> i32 { | |
265 | ||
266 | some_closure(1) | |
267 | } | |
1a4d82fc | 268 | |
c34b1796 | 269 | let answer = call_with_one(|x| x + 2); |
1a4d82fc | 270 | |
c34b1796 | 271 | assert_eq!(3, answer); |
1a4d82fc JJ |
272 | ``` |
273 | ||
9cc50fc6 | 274 | We pass our closure, `|x| x + 2`, to `call_with_one`. It does what it |
c34b1796 | 275 | suggests: it calls the closure, giving it `1` as an argument. |
1a4d82fc | 276 | |
bd371182 | 277 | Let’s examine the signature of `call_with_one` in more depth: |
1a4d82fc | 278 | |
c34b1796 AL |
279 | ```rust |
280 | fn call_with_one<F>(some_closure: F) -> i32 | |
281 | # where F : Fn(i32) -> i32 { | |
282 | # some_closure(1) } | |
1a4d82fc JJ |
283 | ``` |
284 | ||
c34b1796 | 285 | We take one parameter, and it has the type `F`. We also return a `i32`. This part |
bd371182 | 286 | isn’t interesting. The next part is: |
1a4d82fc | 287 | |
c34b1796 AL |
288 | ```rust |
289 | # fn call_with_one<F>(some_closure: F) -> i32 | |
290 | where F : Fn(i32) -> i32 { | |
291 | # some_closure(1) } | |
292 | ``` | |
293 | ||
b039eaaf SL |
294 | Because `Fn` is a trait, we can bound our generic with it. In this case, our |
295 | closure takes a `i32` as an argument and returns an `i32`, and so the generic | |
296 | bound we use is `Fn(i32) -> i32`. | |
1a4d82fc | 297 | |
bd371182 AL |
298 | There’s one other key point here: because we’re bounding a generic with a |
299 | trait, this will get monomorphized, and therefore, we’ll be doing static | |
300 | dispatch into the closure. That’s pretty neat. In many languages, closures are | |
c34b1796 AL |
301 | inherently heap allocated, and will always involve dynamic dispatch. In Rust, |
302 | we can stack allocate our closure environment, and statically dispatch the | |
303 | call. This happens quite often with iterators and their adapters, which often | |
304 | take closures as arguments. | |
1a4d82fc | 305 | |
c34b1796 AL |
306 | Of course, if we want dynamic dispatch, we can get that too. A trait object |
307 | handles this case, as usual: | |
1a4d82fc | 308 | |
c34b1796 AL |
309 | ```rust |
310 | fn call_with_one(some_closure: &Fn(i32) -> i32) -> i32 { | |
311 | some_closure(1) | |
1a4d82fc | 312 | } |
c34b1796 AL |
313 | |
314 | let answer = call_with_one(&|x| x + 2); | |
315 | ||
316 | assert_eq!(3, answer); | |
1a4d82fc JJ |
317 | ``` |
318 | ||
c34b1796 AL |
319 | Now we take a trait object, a `&Fn`. And we have to make a reference |
320 | to our closure when we pass it to `call_with_one`, so we use `&||`. | |
1a4d82fc | 321 | |
e9174d1e SL |
322 | # Function pointers and closures |
323 | ||
324 | A function pointer is kind of like a closure that has no environment. As such, | |
325 | you can pass a function pointer to any function expecting a closure argument, | |
326 | and it will work: | |
327 | ||
328 | ```rust | |
329 | fn call_with_one(some_closure: &Fn(i32) -> i32) -> i32 { | |
330 | some_closure(1) | |
331 | } | |
332 | ||
333 | fn add_one(i: i32) -> i32 { | |
334 | i + 1 | |
335 | } | |
336 | ||
337 | let f = add_one; | |
338 | ||
339 | let answer = call_with_one(&f); | |
340 | ||
341 | assert_eq!(2, answer); | |
342 | ``` | |
343 | ||
344 | In this example, we don’t strictly need the intermediate variable `f`, | |
345 | the name of the function works just fine too: | |
346 | ||
347 | ```ignore | |
348 | let answer = call_with_one(&add_one); | |
349 | ``` | |
350 | ||
c34b1796 | 351 | # Returning closures |
1a4d82fc | 352 | |
c34b1796 AL |
353 | It’s very common for functional-style code to return closures in various |
354 | situations. If you try to return a closure, you may run into an error. At | |
bd371182 | 355 | first, it may seem strange, but we’ll figure it out. Here’s how you’d probably |
c34b1796 | 356 | try to return a closure from a function: |
1a4d82fc | 357 | |
c34b1796 | 358 | ```rust,ignore |
62682a34 SL |
359 | fn factory() -> (Fn(i32) -> i32) { |
360 | let num = 5; | |
1a4d82fc | 361 | |
62682a34 | 362 | |x| x + num |
1a4d82fc JJ |
363 | } |
364 | ||
c34b1796 AL |
365 | let f = factory(); |
366 | ||
62682a34 SL |
367 | let answer = f(1); |
368 | assert_eq!(6, answer); | |
1a4d82fc JJ |
369 | ``` |
370 | ||
c34b1796 AL |
371 | This gives us these long, related errors: |
372 | ||
373 | ```text | |
54a0048b | 374 | error: the trait bound `core::ops::Fn(i32) -> i32 : core::marker::Sized` is not satisfied [E0277] |
62682a34 SL |
375 | fn factory() -> (Fn(i32) -> i32) { |
376 | ^~~~~~~~~~~~~~~~ | |
377 | note: `core::ops::Fn(i32) -> i32` does not have a constant size known at compile-time | |
378 | fn factory() -> (Fn(i32) -> i32) { | |
379 | ^~~~~~~~~~~~~~~~ | |
54a0048b | 380 | error: the trait bound `core::ops::Fn(i32) -> i32 : core::marker::Sized` is not satisfied [E0277] |
62682a34 SL |
381 | let f = factory(); |
382 | ^ | |
383 | note: `core::ops::Fn(i32) -> i32` does not have a constant size known at compile-time | |
384 | let f = factory(); | |
385 | ^ | |
c34b1796 | 386 | ``` |
1a4d82fc | 387 | |
c34b1796 AL |
388 | In order to return something from a function, Rust needs to know what |
389 | size the return type is. But since `Fn` is a trait, it could be various | |
390 | things of various sizes: many different types can implement `Fn`. An easy | |
391 | way to give something a size is to take a reference to it, as references | |
bd371182 | 392 | have a known size. So we’d write this: |
1a4d82fc | 393 | |
c34b1796 | 394 | ```rust,ignore |
62682a34 SL |
395 | fn factory() -> &(Fn(i32) -> i32) { |
396 | let num = 5; | |
c34b1796 | 397 | |
62682a34 | 398 | |x| x + num |
1a4d82fc | 399 | } |
c34b1796 AL |
400 | |
401 | let f = factory(); | |
402 | ||
62682a34 SL |
403 | let answer = f(1); |
404 | assert_eq!(6, answer); | |
c34b1796 AL |
405 | ``` |
406 | ||
407 | But we get another error: | |
408 | ||
409 | ```text | |
410 | error: missing lifetime specifier [E0106] | |
411 | fn factory() -> &(Fn(i32) -> i32) { | |
412 | ^~~~~~~~~~~~~~~~~ | |
1a4d82fc JJ |
413 | ``` |
414 | ||
c34b1796 | 415 | Right. Because we have a reference, we need to give it a lifetime. But |
b039eaaf SL |
416 | our `factory()` function takes no arguments, so |
417 | [elision](lifetimes.html#lifetime-elision) doesn’t kick in here. Then what | |
418 | choices do we have? Try `'static`: | |
1a4d82fc | 419 | |
c34b1796 AL |
420 | ```rust,ignore |
421 | fn factory() -> &'static (Fn(i32) -> i32) { | |
422 | let num = 5; | |
85aaf69f | 423 | |
c34b1796 | 424 | |x| x + num |
85aaf69f SL |
425 | } |
426 | ||
c34b1796 AL |
427 | let f = factory(); |
428 | ||
429 | let answer = f(1); | |
430 | assert_eq!(6, answer); | |
431 | ``` | |
432 | ||
433 | But we get another error: | |
434 | ||
435 | ```text | |
436 | error: mismatched types: | |
437 | expected `&'static core::ops::Fn(i32) -> i32`, | |
b039eaaf | 438 | found `[closure@<anon>:7:9: 7:20]` |
c34b1796 AL |
439 | (expected &-ptr, |
440 | found closure) [E0308] | |
441 | |x| x + num | |
442 | ^~~~~~~~~~~ | |
443 | ||
444 | ``` | |
445 | ||
bd371182 | 446 | This error is letting us know that we don’t have a `&'static Fn(i32) -> i32`, |
b039eaaf | 447 | we have a `[closure@<anon>:7:9: 7:20]`. Wait, what? |
c34b1796 AL |
448 | |
449 | Because each closure generates its own environment `struct` and implementation | |
9cc50fc6 | 450 | of `Fn` and friends, these types are anonymous. They exist solely for |
b039eaaf | 451 | this closure. So Rust shows them as `closure@<anon>`, rather than some |
c34b1796 AL |
452 | autogenerated name. |
453 | ||
b039eaaf SL |
454 | The error also points out that the return type is expected to be a reference, |
455 | but what we are trying to return is not. Further, we cannot directly assign a | |
456 | `'static` lifetime to an object. So we'll take a different approach and return | |
457 | a ‘trait object’ by `Box`ing up the `Fn`. This _almost_ works: | |
c34b1796 AL |
458 | |
459 | ```rust,ignore | |
460 | fn factory() -> Box<Fn(i32) -> i32> { | |
461 | let num = 5; | |
462 | ||
463 | Box::new(|x| x + num) | |
85aaf69f | 464 | } |
c34b1796 AL |
465 | # fn main() { |
466 | let f = factory(); | |
467 | ||
468 | let answer = f(1); | |
469 | assert_eq!(6, answer); | |
470 | # } | |
85aaf69f SL |
471 | ``` |
472 | ||
b039eaaf | 473 | There’s just one last problem: |
85aaf69f | 474 | |
c34b1796 | 475 | ```text |
62682a34 SL |
476 | error: closure may outlive the current function, but it borrows `num`, |
477 | which is owned by the current function [E0373] | |
c34b1796 AL |
478 | Box::new(|x| x + num) |
479 | ^~~~~~~~~~~ | |
480 | ``` | |
481 | ||
b039eaaf SL |
482 | Well, as we discussed before, closures borrow their environment. And in this |
483 | case, our environment is based on a stack-allocated `5`, the `num` variable | |
484 | binding. So the borrow has a lifetime of the stack frame. So if we returned | |
485 | this closure, the function call would be over, the stack frame would go away, | |
486 | and our closure is capturing an environment of garbage memory! With one last | |
487 | fix, we can make this work: | |
85aaf69f | 488 | |
c34b1796 AL |
489 | ```rust |
490 | fn factory() -> Box<Fn(i32) -> i32> { | |
491 | let num = 5; | |
85aaf69f | 492 | |
c34b1796 AL |
493 | Box::new(move |x| x + num) |
494 | } | |
495 | # fn main() { | |
496 | let f = factory(); | |
497 | ||
498 | let answer = f(1); | |
499 | assert_eq!(6, answer); | |
500 | # } | |
501 | ``` | |
85aaf69f | 502 | |
c34b1796 | 503 | By making the inner closure a `move Fn`, we create a new stack frame for our |
54a0048b | 504 | closure. By `Box`ing it up, we’ve given it a known size, allowing it to |
c34b1796 | 505 | escape our stack frame. |