add a note to the README about this being a tutorial for an ancient version of Rust
[rust-101.git] / src / part10.rs
1 // Rust-101, Part 10: Closures
2 // ===========================
3
4 use std::fmt;
5 use part05::BigInt;
6
7 //@ Assume we want to write a function that does *something* on, say, every digit of a `BigInt`.
8 //@ We will then need a way to express the action that we want to be taken, and to pass this to
9 //@ our function. In Rust, a natural first attempt to express this is to have a trait for it.
10
11 // So, let us define a trait that demands that the type provides some method `do_action` on digits.
12 //@ This immediately raises the question: How do we pass `self` to that function? Owned, shared
13 //@ reference, or mutable reference? The typical strategy to answer this question is to use the
14 //@ strongest type that still works. Certainly, passing `self` in owned form does not work: Then
15 //@ the function would consume `self`, and we could not call it again, on the second digit. So
16 //@ let's go with a mutable reference.
17 trait Action {
18     fn do_action(&mut self, digit: u64);
19 }
20
21 // Now we can write a function that takes some `a` of a type `A` such that we can call `do_action`
22 // on `a`, passing it every digit.
23 impl BigInt {
24     fn act_v1<A: Action>(&self, mut a: A) {
25         //@ Remember that the `mut` above is just an annotation to Rust, telling it that we're okay
26         //@ with `a` being mutated. Calling `do_action` on `a` takes a mutable reference, so
27         //@ mutation could indeed happen.
28         for digit in self {
29             a.do_action(digit);                                     /*@*/
30         }
31     }
32 }
33
34 //@ As the next step, we need to come up with some action, and write an appropriate implementation
35 //@ of `Action` for it. So, let's say we want to print every digit, and to make this less boring,
36 //@ we want the digits to be prefixed by some arbitrary string. `do_action` has to know this
37 //@ string, so we store it in `self`.
38 struct PrintWithString {
39     prefix: String,
40 }
41
42 impl Action for PrintWithString {
43     // Here we perform the actual printing of the prefix and the digit. We're not making use of our
44     // ability to change `self` here, but we could replace the prefix if we wanted.
45     fn do_action(&mut self, digit: u64) {
46         println!("{}{}", self.prefix, digit);                       /*@*/
47     }
48 }
49
50 // Finally, this function takes a `BigInt` and a prefix, and prints the digits with the given prefix.
51 //@ It does so by creating an instance of `PrintWithString`, giving it the prefix, and then passing
52 //@ that to `act_v1`. Since `PrintWithString` implements `Action`, Rust now knows what to do.
53 fn print_with_prefix_v1(b: &BigInt, prefix: String) {
54     let my_action = PrintWithString { prefix: prefix };
55     b.act_v1(my_action);
56 }
57
58 // Here's a small main function, demonstrating the code above in action. Remember to edit `main.rs`
59 // to run it.
60 pub fn main() {
61     let bignum = BigInt::new(1 << 63) + BigInt::new(1 << 16) + BigInt::new(1 << 63);
62     print_with_prefix_v1(&bignum, "Digit: ".to_string());
63 }
64
65 // ## Closures
66 //@ Now, as it turns out, this pattern of describing some form of an action, that can carry in
67 //@ additional data, is very common. In general, this is called a *closure*. Closures take some
68 //@ arguments and produce a result, and they have an *environment* they can use, which corresponds
69 //@ to the type `PrintWithString` (or any other type implementing `Action`).
70 //@ Again we have the choice of passing this environment in owned or borrowed form, so there are
71 //@ three traits for closures in Rust: `Fn`-closures get a shared reference, `FnMut`-closures get a
72 //@ mutable reference, and `FnOnce`-closures consume their environment (and can hence be called
73 //@ only once). The syntax for a closure trait which takes arguments of type `T1`, `T2`, ... and
74 //@ returns something of type `U` is `Fn(T1, T2, ...) -> U`.
75
76 // This defines `act` very similar to above, but now we demand `A` to be the type of a closure that
77 // mutates its borrowed environment, takes a digit, and returns nothing.
78 impl BigInt {
79     fn act<A: FnMut(u64)>(&self, mut a: A) {
80         for digit in self {
81             // We can call closures as if they were functions - but really, what's happening here
82             // is translated to essentially what we wrote above, in `act_v1`.
83             a(digit);                                               /*@*/
84         }
85     }
86 }
87
88 // Now that we saw how to write a function that operates on closures, let's see how to write a
89 // closure.
90 pub fn print_with_prefix(b: &BigInt, prefix: String) {
91     //@ The syntax for closures is `|arg1, arg2, ...| code`. Notice that the closure can reference
92     //@ variables like `prefix` that it did not take as argument - variables that happen to be
93     //@ present *outside* of the closure. We say that the closure *captures* variables.
94     //@ Rust will now automatically create a type (like `PrintWithString`) for the environment of
95     //@ the closure with fields for every captured variable, implement the closure trait for this
96     //@ type such that the action performed is given by the code of the closure, and finally it
97     //@ will instantiate the environment type here at the definition site of the closure and fill
98     //@ it appropriately.
99     b.act(|digit| println!("{}{}", prefix, digit) );
100 }
101 // You can change `main` to call this function, and you should notice - nothing, no difference in
102 // behavior. But we wrote much less boilerplate code!
103
104 // Remember that we decided to use the `FnMut` trait above? This means our closure could actually
105 // mutate its environment. For example, we can use that to count the digits as they are printed.
106 pub fn print_and_count(b: &BigInt) {
107     let mut count: usize = 0;
108     //@ This time, the environment will contain a field of type `&mut usize`, that will be
109     //@ initialized with a mutable reference of `count`. The closure, since it mutably borrows its
110     //@ environment, is able to access this field and mutate `count` through it. Once `act`
111     //@ returns, the closure is destroyed and `count` is no longer borrowed.
112     //@ Because closures compile down to normal types, all the borrow checking continues to work as
113     //@ usually, and we cannot accidentally leak a closure somewhere that still contains, in its
114     //@ environment, a dead reference.
115     b.act(|digit| { println!("{}: {}", count, digit); count = count +1; } );
116     println!("There are {} digits", count);
117 }
118
119 // ## Fun with iterators and closures
120 //@ If you are familiar with functional languages, you are probably aware that one can have lots of
121 //@ fun with iterators and closures. Rust provides a whole lot of methods on iterators that allow
122 //@ us to write pretty functional-style list manipulation.
123
124 // Let's say we want to write a function that increments every entry of a `Vec` by some number,
125 // then looks for numbers larger than some threshold, and prints them.
126 fn inc_print_threshold(v: &Vec<i32>, offset: i32, threshold: i32) {
127     //@ `map` takes a closure that is applied to every element of the iterator. `filter` removes
128     //@ elements from the iterator that do not pass the test given by the closure.
129     //@ 
130     //@ Since all these closures compile down to the pattern described above, there is actually no
131     //@ heap allocation going on here. This makes closures very efficient, and it makes
132     //@ optimization fairly trivial: The resulting code will look like you hand-rolled the loop in
133     //@ C.
134     for i in v.iter().map(|n| *n + offset).filter(|n| *n > threshold) {
135         println!("{}", i);
136     }
137 }
138
139 // Sometimes it is useful to know both the position of some element in a list, and its value.
140 // That's where the `enumerate` function helps.
141 fn print_enumerated<T: fmt::Display>(v: &Vec<T>) {
142     //@ `enumerate` turns an iterator over `T` into an iterator over `(usize, T)`, where the first
143     //@ element just counts the position in the iterator. We can do pattern matching right in the
144     //@ loop header to obtain names for both the position, and the value.
145     for (i, t) in v.iter().enumerate() {
146         println!("Position {}: {}", i, t);
147     }
148 }
149
150 // And as a final example, one can also collect all elements of an iterator, and put them, e.g., in a vector.
151 fn filter_vec_by_divisor(v: &Vec<i32>, divisor: i32) -> Vec<i32> {
152     //@ Here, the return type of `collect` is inferred based on the return type of our function. In
153     //@ general, it can return anything implementing
154     //@ [`FromIterator`](https://doc.rust-lang.org/stable/std/iter/trait.FromIterator.html).
155     //@ Notice that `iter` gives us an iterator over borrowed `i32`, but we want to own them for
156     //@ the result, so we insert a `map` to dereference.
157     v.iter().map(|n| *n).filter(|n| *n % divisor == 0).collect()    /*@*/
158 }
159
160 // **Exercise 10.1**: Look up the
161 // [documentation of `Iterator`](https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html)
162 // to learn about more functions that can act on iterators. Try using some of them. What about a
163 // function that sums the even numbers of an iterator? Or a function that computes the product of
164 // those numbers that sit at odd positions? A function that checks whether a vector contains a
165 // certain number? Whether all numbers are smaller than some threshold? Be creative!
166
167 // **Exercise 10.2**: We started the journey in Part 02 with `SomethingOrNothing<T>`, and later
168 // learned about `Option<T>` in Part 04. `Option<T>` also has a `map` function.
169 // [Read its documentation here.](https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.map)
170 // Which functions in previous parts can you rewrite to use `map` instead?
171 // (Hint: read the source code of `map`, and see if the pattern appears in your own code.)
172 // Bonus: [`test_invariant` in Part 05](part05.html#section-6) doesn't use `match`,
173 // but can you still find a way to rewrite it with `map`?
174
175 //@ [index](main.html) | [previous](part09.html) | [raw source](workspace/src/part10.rs) |
176 //@ [next](part11.html)