1
1
% Closures
2
2
3
3
Rust not only has named functions, but anonymous functions as well. Anonymous
4
- functions that have an associated environment are called ' closures' , because they
4
+ functions that have an associated environment are called ‘ closures’ , because they
5
5
close over an environment. Rust has a really great implementation of them, as
6
- we' ll see.
6
+ we’ ll see.
7
7
8
8
# Syntax
9
9
@@ -15,7 +15,7 @@ let plus_one = |x: i32| x + 1;
15
15
assert_eq! (2 , plus_one (1 ));
16
16
```
17
17
18
- We create a binding, ` plus_one ` , and assign it to a closure. The closure' s
18
+ We create a binding, ` plus_one ` , and assign it to a closure. The closure’ s
19
19
arguments go between the pipes (` | ` ), and the body is an expression, in this
20
20
case, ` x + 1 ` . Remember that ` { } ` is an expression, so we can have multi-line
21
21
closures too:
@@ -33,7 +33,7 @@ let plus_two = |x| {
33
33
assert_eq! (4 , plus_two (2 ));
34
34
```
35
35
36
- You' ll notice a few things about closures that are a bit different than regular
36
+ You’ ll notice a few things about closures that are a bit different than regular
37
37
functions defined with ` fn ` . The first of which is that we did not need to
38
38
annotate the types of arguments the closure takes or the values it returns. We
39
39
can:
@@ -44,13 +44,13 @@ let plus_one = |x: i32| -> i32 { x + 1 };
44
44
assert_eq! (2 , plus_one (1 ));
45
45
```
46
46
47
- But we don' t have to. Why is this? Basically, it was chosen for ergonomic reasons.
47
+ But we don’ t have to. Why is this? Basically, it was chosen for ergonomic reasons.
48
48
While specifying the full type for named functions is helpful with things like
49
49
documentation and type inference, the types of closures are rarely documented
50
50
since they’re anonymous, and they don’t cause the kinds of error-at-a-distance
51
51
that inferring named function types can.
52
52
53
- The second is that the syntax is similar, but a bit different. I' ve added spaces
53
+ The second is that the syntax is similar, but a bit different. I’ ve added spaces
54
54
here to make them look a little closer:
55
55
56
56
``` rust
@@ -59,11 +59,11 @@ let plus_one_v2 = |x: i32 | -> i32 { x + 1 };
59
59
let plus_one_v3 = | x : i32 | x + 1 ;
60
60
```
61
61
62
- Small differences, but they' re similar in ways.
62
+ Small differences, but they’ re similar in ways.
63
63
64
64
# Closures and their environment
65
65
66
- Closures are called such because they ' close over their environment.' It
66
+ Closures are called such because they ‘ close over their environment’. It
67
67
looks like this:
68
68
69
69
``` rust
@@ -105,7 +105,7 @@ fn main() {
105
105
^
106
106
```
107
107
108
- A verbose yet helpful error message! As it says, we can' t take a mutable borrow
108
+ A verbose yet helpful error message! As it says, we can’ t take a mutable borrow
109
109
on ` num ` because the closure is already borrowing it. If we let the closure go
110
110
out of scope, we can:
111
111
@@ -140,7 +140,7 @@ let takes_nums = || nums;
140
140
```
141
141
142
142
` Vec<T> ` has ownership over its contents, and therefore, when we refer to it
143
- in our closure, we have to take ownership of ` nums ` . It' s the same as if we' d
143
+ in our closure, we have to take ownership of ` nums ` . It’ s the same as if we’ d
144
144
passed ` nums ` to a function that took ownership of it.
145
145
146
146
## ` move ` closures
@@ -156,7 +156,7 @@ let owns_num = move |x: i32| x + num;
156
156
157
157
Now, even though the keyword is ` move ` , the variables follow normal move semantics.
158
158
In this case, ` 5 ` implements ` Copy ` , and so ` owns_num ` takes ownership of a copy
159
- of ` num ` . So what' s the difference?
159
+ of ` num ` . So what’ s the difference?
160
160
161
161
``` rust
162
162
let mut num = 5 ;
@@ -171,11 +171,11 @@ assert_eq!(10, num);
171
171
```
172
172
173
173
So in this case, our closure took a mutable reference to ` num ` , and then when
174
- we called ` add_num ` , it mutated the underlying value, as we' d expect. We also
174
+ we called ` add_num ` , it mutated the underlying value, as we’ d expect. We also
175
175
needed to declare ` add_num ` as ` mut ` too, because we’re mutating its
176
176
environment.
177
177
178
- If we change to a ` move ` closure, it' s different:
178
+ If we change to a ` move ` closure, it’ s different:
179
179
180
180
``` rust
181
181
let mut num = 5 ;
@@ -203,8 +203,8 @@ you tons of control over what your code does, and closures are no different.
203
203
204
204
# Closure implementation
205
205
206
- Rust' s implementation of closures is a bit different than other languages. They
207
- are effectively syntax sugar for traits. You' ll want to make sure to have read
206
+ Rust’ s implementation of closures is a bit different than other languages. They
207
+ are effectively syntax sugar for traits. You’ ll want to make sure to have read
208
208
the [ traits chapter] [ traits ] before this one, as well as the chapter on [ trait
209
209
objects] [ trait-objects ] .
210
210
@@ -237,9 +237,9 @@ pub trait FnOnce<Args> {
237
237
# }
238
238
```
239
239
240
- You' ll notice a few differences between these traits, but a big one is ` self ` :
240
+ You’ ll notice a few differences between these traits, but a big one is ` self ` :
241
241
` Fn ` takes ` &self ` , ` FnMut ` takes ` &mut self ` , and ` FnOnce ` takes ` self ` . This
242
- covers all three kinds of ` self ` via the usual method call syntax. But we' ve
242
+ covers all three kinds of ` self ` via the usual method call syntax. But we’ ve
243
243
split them up into three traits, rather than having a single one. This gives us
244
244
a large amount of control over what kind of closures we can take.
245
245
@@ -253,7 +253,7 @@ Now that we know that closures are traits, we already know how to accept and
253
253
return closures: just like any other trait!
254
254
255
255
This also means that we can choose static vs dynamic dispatch as well. First,
256
- let' s write a function which takes something callable, calls it, and returns
256
+ let’ s write a function which takes something callable, calls it, and returns
257
257
the result:
258
258
259
259
``` rust
@@ -271,7 +271,7 @@ assert_eq!(3, answer);
271
271
We pass our closure, ` |x| x + 2 ` , to ` call_with_one ` . It just does what it
272
272
suggests: it calls the closure, giving it ` 1 ` as an argument.
273
273
274
- Let' s examine the signature of ` call_with_one ` in more depth:
274
+ Let’ s examine the signature of ` call_with_one ` in more depth:
275
275
276
276
``` rust
277
277
fn call_with_one <F >(some_closure : F ) -> i32
@@ -280,7 +280,7 @@ fn call_with_one<F>(some_closure: F) -> i32
280
280
```
281
281
282
282
We take one parameter, and it has the type ` F ` . We also return a ` i32 ` . This part
283
- isn' t interesting. The next part is:
283
+ isn’ t interesting. The next part is:
284
284
285
285
``` rust
286
286
# fn call_with_one <F >(some_closure : F ) -> i32
@@ -292,9 +292,9 @@ Because `Fn` is a trait, we can bound our generic with it. In this case, our clo
292
292
takes a ` i32 ` as an argument and returns an ` i32 ` , and so the generic bound we use
293
293
is ` Fn(i32) -> i32 ` .
294
294
295
- There' s one other key point here: because we' re bounding a generic with a
296
- trait, this will get monomorphized, and therefore, we' ll be doing static
297
- dispatch into the closure. That' s pretty neat. In many langauges, closures are
295
+ There’ s one other key point here: because we’ re bounding a generic with a
296
+ trait, this will get monomorphized, and therefore, we’ ll be doing static
297
+ dispatch into the closure. That’ s pretty neat. In many langauges, closures are
298
298
inherently heap allocated, and will always involve dynamic dispatch. In Rust,
299
299
we can stack allocate our closure environment, and statically dispatch the
300
300
call. This happens quite often with iterators and their adapters, which often
@@ -320,7 +320,7 @@ to our closure when we pass it to `call_with_one`, so we use `&||`.
320
320
321
321
It’s very common for functional-style code to return closures in various
322
322
situations. If you try to return a closure, you may run into an error. At
323
- first, it may seem strange, but we' ll figure it out. Here' s how you' d probably
323
+ first, it may seem strange, but we’ ll figure it out. Here’ s how you’ d probably
324
324
try to return a closure from a function:
325
325
326
326
``` rust,ignore
@@ -361,7 +361,7 @@ In order to return something from a function, Rust needs to know what
361
361
size the return type is. But since ` Fn ` is a trait, it could be various
362
362
things of various sizes: many different types can implement ` Fn ` . An easy
363
363
way to give something a size is to take a reference to it, as references
364
- have a known size. So we' d write this:
364
+ have a known size. So we’ d write this:
365
365
366
366
``` rust,ignore
367
367
fn factory() -> &(Fn(i32) -> Vec<i32>) {
@@ -385,7 +385,7 @@ fn factory() -> &(Fn(i32) -> i32) {
385
385
```
386
386
387
387
Right. Because we have a reference, we need to give it a lifetime. But
388
- our ` factory() ` function takes no arguments, so elision doesn' t kick in
388
+ our ` factory() ` function takes no arguments, so elision doesn’ t kick in
389
389
here. What lifetime can we choose? ` 'static ` :
390
390
391
391
``` rust,ignore
@@ -414,15 +414,15 @@ error: mismatched types:
414
414
415
415
```
416
416
417
- This error is letting us know that we don' t have a ` &'static Fn(i32) -> i32 ` ,
417
+ This error is letting us know that we don’ t have a ` &'static Fn(i32) -> i32 ` ,
418
418
we have a ` [closure <anon>:7:9: 7:20] ` . Wait, what?
419
419
420
420
Because each closure generates its own environment ` struct ` and implementation
421
421
of ` Fn ` and friends, these types are anonymous. They exist just solely for
422
422
this closure. So Rust shows them as ` closure <anon> ` , rather than some
423
423
autogenerated name.
424
424
425
- But why doesn' t our closure implement ` &'static Fn ` ? Well, as we discussed before,
425
+ But why doesn’ t our closure implement ` &'static Fn ` ? Well, as we discussed before,
426
426
closures borrow their environment. And in this case, our environment is based
427
427
on a stack-allocated ` 5 ` , the ` num ` variable binding. So the borrow has a lifetime
428
428
of the stack frame. So if we returned this closure, the function call would be
@@ -445,7 +445,7 @@ assert_eq!(6, answer);
445
445
# }
446
446
```
447
447
448
- We use a trait object, by ` Box ` ing up the ` Fn ` . There' s just one last problem:
448
+ We use a trait object, by ` Box ` ing up the ` Fn ` . There’ s just one last problem:
449
449
450
450
``` text
451
451
error: `num` does not live long enough
@@ -471,5 +471,5 @@ assert_eq!(6, answer);
471
471
```
472
472
473
473
By making the inner closure a ` move Fn ` , we create a new stack frame for our
474
- closure. By ` Box ` ing it up, we' ve given it a known size, and allowing it to
474
+ closure. By ` Box ` ing it up, we’ ve given it a known size, and allowing it to
475
475
escape our stack frame.
0 commit comments