Skip to content

Commit b3adc3b

Browse files
authored
Clarify Fn traits slide (#2333)
Improve naming and always capture something in our closures; explain other details/variations in speaker notes. Fixes #2251.
1 parent 432f7bc commit b3adc3b

File tree

1 file changed

+25
-20
lines changed

1 file changed

+25
-20
lines changed

src/std-traits/closures.md

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,39 +10,41 @@ implement special [`Fn`](https://doc.rust-lang.org/std/ops/trait.Fn.html),
1010
[`FnOnce`](https://doc.rust-lang.org/std/ops/trait.FnOnce.html) traits:
1111

1212
```rust,editable
13-
fn apply_with_log(func: impl FnOnce(i32) -> i32, input: i32) -> i32 {
14-
println!("Calling function on {input}");
15-
func(input)
13+
fn apply_and_log(func: impl FnOnce(i32) -> i32, func_name: &str, input: i32) {
14+
println!("Calling {func_name}({input}): {}", func(input))
1615
}
1716
1817
fn main() {
19-
let add_3 = |x| x + 3;
20-
println!("add_3: {}", apply_with_log(add_3, 10));
21-
println!("add_3: {}", apply_with_log(add_3, 20));
18+
let n = 3;
19+
let add_3 = |x| x + n;
20+
apply_and_log(&add_3, "add_3", 10);
21+
apply_and_log(&add_3, "add_3", 20);
2222
2323
let mut v = Vec::new();
2424
let mut accumulate = |x: i32| {
2525
v.push(x);
2626
v.iter().sum::<i32>()
2727
};
28-
println!("accumulate: {}", apply_with_log(&mut accumulate, 4));
29-
println!("accumulate: {}", apply_with_log(&mut accumulate, 5));
28+
apply_and_log(&mut accumulate, "accumulate", 4);
29+
apply_and_log(&mut accumulate, "accumulate", 5);
3030
3131
let multiply_sum = |x| x * v.into_iter().sum::<i32>();
32-
println!("multiply_sum: {}", apply_with_log(multiply_sum, 3));
32+
apply_and_log(multiply_sum, "multiply_sum", 3);
3333
}
3434
```
3535

3636
<details>
3737

38-
An `Fn` (e.g. `add_3`) neither consumes nor mutates captured values, or perhaps
39-
captures nothing at all. It can be called multiple times concurrently.
38+
An `Fn` (e.g. `add_3`) neither consumes nor mutates captured values. It can be
39+
called needing only a shared reference to the closure, which means the closure
40+
can be executed repeatedly and even concurrently.
4041

41-
An `FnMut` (e.g. `accumulate`) might mutate captured values. You can call it
42-
multiple times, but not concurrently.
42+
An `FnMut` (e.g. `accumulate`) might mutate captured values. The closure object
43+
is accessed via exclusive reference, so it can be called repeatedly but not
44+
concurrently.
4345

44-
If you have an `FnOnce` (e.g. `multiply_sum`), you may only call it once. It
45-
might consume captured values.
46+
If you have an `FnOnce` (e.g. `multiply_sum`), you may only call it once. Doing
47+
so consumes the closure and any values captured by move.
4648

4749
`FnMut` is a subtype of `FnOnce`. `Fn` is a subtype of `FnMut` and `FnOnce`.
4850
I.e. you can use an `FnMut` wherever an `FnOnce` is called for, and you can use
@@ -52,14 +54,17 @@ When you define a function that takes a closure, you should take `FnOnce` if you
5254
can (i.e. you call it once), or `FnMut` else, and last `Fn`. This allows the
5355
most flexibility for the caller.
5456

55-
In contrast, when you have a closure, the most flexible you can have is `Fn` (it
56-
can be passed everywhere), then `FnMut`, and lastly `FnOnce`.
57+
In contrast, when you have a closure, the most flexible you can have is `Fn`
58+
(which can be passed to a consumer of any of the 3 closure traits), then
59+
`FnMut`, and lastly `FnOnce`.
5760

5861
The compiler also infers `Copy` (e.g. for `add_3`) and `Clone` (e.g.
59-
`multiply_sum`), depending on what the closure captures.
62+
`multiply_sum`), depending on what the closure captures. Function pointers
63+
(references to `fn` items) implement `Copy` and `Fn`.
6064

61-
By default, closures will capture by reference if they can. The `move` keyword
62-
makes them capture by value.
65+
By default, closures will capture each variable from an outer scope by the least
66+
demanding form of access they can (by shared reference if possible, then
67+
exclusive reference, then by move). The `move` keyword forces capture by value.
6368

6469
```rust,editable
6570
fn make_greeter(prefix: String) -> impl Fn(&str) {

0 commit comments

Comments
 (0)