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