complete part 10
[rust-101.git] / workspace / 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
9 // So, let us define a trait that demands that the type provides some method `do_action` on digits.
10 trait Action {
11     fn do_action(&mut self, digit: u64);
12 }
13
14 // 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.
15 impl BigInt {
16     fn act_v1<A: Action>(&self, mut a: A) {
17         for digit in self {
18             a.do_action(digit);
19         }
20     }
21 }
22
23 struct PrintWithString {
24     prefix: String,
25 }
26
27 impl Action for PrintWithString {
28     // Here we perform performs the actual printing of the prefix and the digit. We're not making use of our ability to
29     // change `self` here, but we could replace the prefix if we wanted.
30     fn do_action(&mut self, digit: u64) {
31         println!("{}{}", self.prefix, digit);
32     }
33 }
34
35 // Finally, this function takes a `BigInt` and a prefix, and prints the digits with the given prefix.
36 fn print_with_prefix_v1(b: &BigInt, prefix: String) {
37     let my_action = PrintWithString { prefix: prefix };
38     b.act_v1(my_action);
39 }
40
41 // Here's a small main function, demonstrating the code above in action. Remember to edit `main.rs` to run it.
42 pub fn main() {
43     let bignum = BigInt::new(1 << 63) + BigInt::new(1 << 16) + BigInt::new(1 << 63);
44     print_with_prefix_v1(&bignum, "Digit: ".to_string());
45 }
46
47 // ## Closures
48
49 // This defines `act` very similar to above, but now we demand `A` to be the type of a closure that mutates its borrowed environment,
50 // takes a digit, and returns nothing.
51 impl BigInt {
52     fn act<A: FnMut(u64)>(&self, mut a: A) {
53         for digit in self {
54             // 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`.
55             a(digit);
56         }
57     }
58 }
59
60 // Now that we saw how to write a function that operates on closures, let's see how to write a closure.
61 pub fn print_with_prefix(b: &BigInt, prefix: String) {
62     b.act(|digit| println!("{}{}", prefix, digit) );
63 }
64 // You can change `main` to call this function, and you should notice - nothing, no difference in behavior.
65 // But we wrote much less boilerplate code!
66
67 // Remember that we decided to use the `FnMut` trait above? This means our closure could actually mutate its environment.
68 // For example, we can use that to count the digits as they are printed.
69 pub fn print_and_count(b: &BigInt) {
70     let mut count: usize = 0;
71     b.act(|digit| { println!("{}: {}", count, digit); count = count +1; } );
72     println!("There are {} digits", count);
73 }
74
75 // ## Fun with iterators and closures
76
77 // 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.
78 fn inc_print_even(v: &Vec<i32>, offset: i32, threshold: i32) {
79     for i in v.iter().map(|n| n + offset).filter(|n| *n > threshold) {
80         println!("{}", i);
81     }
82 }
83
84 // 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.
85 fn print_enumerated<T: fmt::Display>(v: &Vec<T>) {
86     for (i, t) in v.iter().enumerate() {
87         println!("Position {}: {}", i, t);
88     }
89 }
90
91 // And as a final example, one can also collect all elements of an iterator, and put them, e.g., in a vector.
92 fn filter_vec_by_divisor(v: &Vec<i32>, divisor: i32) -> Vec<i32> {
93     v.iter().filter(|n| *n % divisor == 0).collect()
94 }
95
96 // **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
97 // 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
98 // product of those numbers that sit at odd positions? A function that checks whether a vector contains a certain number? Whether all numbers are
99 // smaller than some threshold? Be creative!
100