Skip to content

Commit 3f64903

Browse files
committed
Merge pull request #594 from mdinger/functionality
Rewrite and expand the closure section
2 parents ae5cc0a + db16b9c commit 3f64903

File tree

21 files changed

+431
-17
lines changed

21 files changed

+431
-17
lines changed
File renamed without changes.
File renamed without changes.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// `F` must implement `Fn` for a closure which takes no
2+
// inputs and returns nothing. Exactly what is required
3+
// for `print`.
4+
fn apply<F>(f: F) where
5+
F: Fn() {
6+
7+
f()
8+
}
9+
10+
fn main() {
11+
let x = 7;
12+
13+
// Capture `x` into an anonymous type and implement
14+
// `Fn` for it. Store it in `print`.
15+
let print = || println!("{}", x);
16+
17+
apply(print);
18+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
Closures succinctly capture variables from enclosing scopes. Does this have
2+
any consequences? It surely does. Observe how using a closure in a function
3+
requires generics, which is necessary because of how they are defined:
4+
5+
```rust
6+
// `F` must be generic.
7+
fn apply<F>(f: F) where
8+
F: FnOnce() {
9+
f()
10+
}
11+
```
12+
13+
When a closure is defined, the compiler implicitly creates a new
14+
anonymous structure to store the captured variables inside, meanwhile
15+
implementing the functionality via one of the `traits`: `Fn`, `FnMut`, or
16+
`FnOnce` for this unknown type. This type is assigned to the variable which
17+
is stored until calling.
18+
19+
Since this new type is of unknown type, any usage in a function will require
20+
generics. However, an unbounded type parameter (`<T>`) would still be ambiguous
21+
and not be allowed. Thus, bounding by one of the `traits`: `Fn`, `FnMut`, or
22+
`FnOnce` (which it implements) is sufficient to specify it's type.
23+
24+
{anonymity.play}
25+
26+
### See also:
27+
28+
[A thorough analysis][thorough_analysis], [`Fn`][fn], [`FnMut`][fn_mut],
29+
and [`FnOnce`][fn_once]
30+
31+
[fn]: http://doc.rust-lang.org/std/ops/trait.Fn.html
32+
[fn_mut]: http://doc.rust-lang.org/std/ops/trait.FnMut.html
33+
[fn_once]: http://doc.rust-lang.org/std/ops/trait.FnOnce.html
34+
[thorough_analysis]: http://huonw.github.io/blog/2015/05/finding-closure-in-rust/
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
fn main() {
2+
let color = "green";
3+
4+
// A closure to print `color` which immediately borrows (`&`)
5+
// `color` and stores the borrow and closure in the `print`
6+
// variable. It will remain borrowed until `print` goes out of
7+
// scope. `println!` only requires `by reference` so it doesn't
8+
// impose anything more restrictive.
9+
let print = || println!("`color`: {}", color);
10+
11+
// Call the closure using the borrow.
12+
print();
13+
print();
14+
15+
let mut count = 0;
16+
17+
// A closure to increment `count` could take either `&mut count`
18+
// or `count` but `&mut count` is less restrictive so it takes
19+
// that. Immediately borrows `count`.
20+
//
21+
// A `mut` is required on `inc` because a `&mut` is stored inside.
22+
// Thus, calling the closure mutates the closure which requires
23+
// a `mut`.
24+
let mut inc = || {
25+
count += 1;
26+
println!("`count`: {}", count);
27+
};
28+
29+
// Call the closure.
30+
inc();
31+
inc();
32+
33+
//let reborrow = &mut count;
34+
// ^ TODO: try uncommenting this line.
35+
36+
// A non-copy type.
37+
let movable = Box::new(3);
38+
39+
// `drop` requires `T` so this must take by value. A copy type
40+
// would copy into the closure leaving the original untouched.
41+
// A non-copy must move and so `movable` immediately moves into
42+
// the closure.
43+
let consume = || {
44+
println!("`movable`: {:?}", movable);
45+
drop(movable);
46+
};
47+
48+
// `eat` consumes the variable so this can only be called once.
49+
consume();
50+
//consume();
51+
// ^ TODO: Try uncommenting this line.
52+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Closures are inherently flexible and will do what the functionality requires
2+
to make the closure work without annotation. This allows capturing to
3+
flexibly adapt to the use case, sometimes moving and sometimes borrowing.
4+
Closures can capture variables:
5+
6+
* by reference: `&T`
7+
* by mutable reference: `&mut T`
8+
* by value: `T`
9+
10+
They preferentially capture variables by reference and only go lower when
11+
required.
12+
13+
{capture.play}
14+
15+
### See also:
16+
17+
[`Box`][box] and [`std::mem::drop`][drop]
18+
19+
[box]: /std/box.html
20+
[drop]: http://doc.rust-lang.org/std/mem/fn.drop.html
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
A brief analysis of a few different `std` library closure examples.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
`Iterator::any` is a function which when passed an iterator, will return
2+
`true` if any element satisfies the predicate. Otherwise `false`. It's
3+
signature:
4+
5+
```rust
6+
pub trait Iterator {
7+
// The type being iterated over.
8+
type Item;
9+
10+
// `any` takes `&mut self` meaning the caller may be borrowed
11+
// and modified, but not consumed.
12+
fn any<F>(&mut self, f: F) -> bool where
13+
// `FnMut` meaning any captured variable may at most be
14+
// modified, not consumed. `Self::Item` states it takes
15+
// arguments to the closure by value.
16+
F: FnMut(Self::Item) -> bool {}
17+
}
18+
```
19+
20+
{iter_any.play}
21+
22+
### See also:
23+
24+
[`Iterator::any`][any]
25+
26+
[any]: http://doc.rust-lang.org/std/iter/trait.Iterator.html#method.any
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
fn main() {
2+
let vec1 = vec![1, 2, 3];
3+
let vec2 = vec![4, 5, 6];
4+
5+
// `iter` yields `&i32`. Destructure to `i32`.
6+
println!("2 in vec1: {}", vec1.iter() .any(|&x| x == 2));
7+
// `into_iter` yields `i32`. No destructuring required.
8+
println!("2 in vec2: {}", vec2.into_iter().any(| x| x == 2));
9+
10+
let array1 = [1, 2, 3];
11+
let array2 = [4, 5, 6];
12+
13+
// `iter()` is normal. `into_iter()` for arrays unusually
14+
// yields `&i32`. These would not normally both be `&`.
15+
println!("2 in array1: {}", array1.iter() .any(|&x| x == 2));
16+
println!("2 in array2: {}", array2.into_iter().any(|&x| x == 2));
17+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
`Iterator::find` is a function which when passed an iterator, will return
2+
the first element which satisfies the predicate as an `Option`. It's
3+
signature:
4+
5+
```rust
6+
pub trait Iterator {
7+
// The type being iterated over.
8+
type Item;
9+
10+
// `any` takes `&mut self` meaning the caller may be borrowed
11+
// and modified, but not consumed.
12+
fn find<P>(&mut self, predicate: P) -> Option<Self::Item> where
13+
// `FnMut` meaning any captured variable may at most be
14+
// modified, not consumed. `&Self::Item` states it takes
15+
// arguments to the closure by reference.
16+
P: FnMut(&Self::Item) -> bool {}
17+
}
18+
```
19+
20+
{iter_find.play}
21+
22+
### See also:
23+
24+
[`Iterator::find`][find]
25+
26+
[find]: http://doc.rust-lang.org/std/iter/trait.Iterator.html#method.find

0 commit comments

Comments
 (0)