]> git.proxmox.com Git - rustc.git/blob - src/doc/book/src/ch13-03-improving-our-io-project.md
New upstream version 1.43.0+dfsg1
[rustc.git] / src / doc / book / src / ch13-03-improving-our-io-project.md
1 ## Improving Our I/O Project
2
3 With this new knowledge about iterators, we can improve the I/O project in
4 Chapter 12 by using iterators to make places in the code clearer and more
5 concise. Let’s look at how iterators can improve our implementation of the
6 `Config::new` function and the `search` function.
7
8 ### Removing a `clone` Using an Iterator
9
10 In Listing 12-6, we added code that took a slice of `String` values and created
11 an instance of the `Config` struct by indexing into the slice and cloning the
12 values, allowing the `Config` struct to own those values. In Listing 13-24,
13 we’ve reproduced the implementation of the `Config::new` function as it was in
14 Listing 12-23:
15
16 <span class="filename">Filename: src/lib.rs</span>
17
18 ```rust,ignore
19 {{#rustdoc_include ../listings/ch13-functional-features/listing-12-23-reproduced/src/lib.rs:ch13}}
20 ```
21
22 <span class="caption">Listing 13-24: Reproduction of the `Config::new` function
23 from Listing 12-23</span>
24
25 At the time, we said not to worry about the inefficient `clone` calls because
26 we would remove them in the future. Well, that time is now!
27
28 We needed `clone` here because we have a slice with `String` elements in the
29 parameter `args`, but the `new` function doesn’t own `args`. To return
30 ownership of a `Config` instance, we had to clone the values from the `query`
31 and `filename` fields of `Config` so the `Config` instance can own its values.
32
33 With our new knowledge about iterators, we can change the `new` function to
34 take ownership of an iterator as its argument instead of borrowing a slice.
35 We’ll use the iterator functionality instead of the code that checks the length
36 of the slice and indexes into specific locations. This will clarify what the
37 `Config::new` function is doing because the iterator will access the values.
38
39 Once `Config::new` takes ownership of the iterator and stops using indexing
40 operations that borrow, we can move the `String` values from the iterator into
41 `Config` rather than calling `clone` and making a new allocation.
42
43 #### Using the Returned Iterator Directly
44
45 Open your I/O project’s *src/main.rs* file, which should look like this:
46
47 <span class="filename">Filename: src/main.rs</span>
48
49 ```rust,ignore
50 {{#rustdoc_include ../listings/ch13-functional-features/listing-12-24-reproduced/src/main.rs:ch13}}
51 ```
52
53 We’ll change the start of the `main` function that we had in Listing 12-24 to
54 the code in Listing 13-25. This won’t compile until we update `Config::new` as
55 well.
56
57 <span class="filename">Filename: src/main.rs</span>
58
59 ```rust,ignore
60 {{#rustdoc_include ../listings/ch13-functional-features/listing-13-25/src/main.rs:here}}
61 ```
62
63 <span class="caption">Listing 13-25: Passing the return value of `env::args` to
64 `Config::new`</span>
65
66 The `env::args` function returns an iterator! Rather than collecting the
67 iterator values into a vector and then passing a slice to `Config::new`, now
68 we’re passing ownership of the iterator returned from `env::args` to
69 `Config::new` directly.
70
71 Next, we need to update the definition of `Config::new`. In your I/O project’s
72 *src/lib.rs* file, let’s change the signature of `Config::new` to look like
73 Listing 13-26. This still won’t compile because we need to update the function
74 body.
75
76 <span class="filename">Filename: src/lib.rs</span>
77
78 ```rust,ignore
79 {{#rustdoc_include ../listings/ch13-functional-features/listing-13-26/src/lib.rs:here}}
80 ```
81
82 <span class="caption">Listing 13-26: Updating the signature of `Config::new` to
83 expect an iterator</span>
84
85 The standard library documentation for the `env::args` function shows that the
86 type of the iterator it returns is `std::env::Args`. We’ve updated the
87 signature of the `Config::new` function so the parameter `args` has the type
88 `std::env::Args` instead of `&[String]`. Because we’re taking ownership of
89 `args` and we’ll be mutating `args` by iterating over it, we can add the `mut`
90 keyword into the specification of the `args` parameter to make it mutable.
91
92 #### Using `Iterator` Trait Methods Instead of Indexing
93
94 Next, we’ll fix the body of `Config::new`. The standard library documentation
95 also mentions that `std::env::Args` implements the `Iterator` trait, so we know
96 we can call the `next` method on it! Listing 13-27 updates the code from
97 Listing 12-23 to use the `next` method:
98
99 <span class="filename">Filename: src/lib.rs</span>
100
101 ```rust
102 {{#rustdoc_include ../listings/ch13-functional-features/listing-13-27/src/lib.rs:here}}
103 ```
104
105 <span class="caption">Listing 13-27: Changing the body of `Config::new` to use
106 iterator methods</span>
107
108 Remember that the first value in the return value of `env::args` is the name of
109 the program. We want to ignore that and get to the next value, so first we call
110 `next` and do nothing with the return value. Second, we call `next` to get the
111 value we want to put in the `query` field of `Config`. If `next` returns a
112 `Some`, we use a `match` to extract the value. If it returns `None`, it means
113 not enough arguments were given and we return early with an `Err` value. We do
114 the same thing for the `filename` value.
115
116 ### Making Code Clearer with Iterator Adaptors
117
118 We can also take advantage of iterators in the `search` function in our I/O
119 project, which is reproduced here in Listing 13-28 as it was in Listing 12-19:
120
121 <span class="filename">Filename: src/lib.rs</span>
122
123 ```rust,ignore
124 {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-19/src/lib.rs:ch13}}
125 ```
126
127 <span class="caption">Listing 13-28: The implementation of the `search`
128 function from Listing 12-19</span>
129
130 We can write this code in a more concise way using iterator adaptor methods.
131 Doing so also lets us avoid having a mutable intermediate `results` vector. The
132 functional programming style prefers to minimize the amount of mutable state to
133 make code clearer. Removing the mutable state might enable a future enhancement
134 to make searching happen in parallel, because we wouldn’t have to manage
135 concurrent access to the `results` vector. Listing 13-29 shows this change:
136
137 <span class="filename">Filename: src/lib.rs</span>
138
139 ```rust,ignore
140 {{#rustdoc_include ../listings/ch13-functional-features/listing-13-29/src/lib.rs:here}}
141 ```
142
143 <span class="caption">Listing 13-29: Using iterator adaptor methods in the
144 implementation of the `search` function</span>
145
146 Recall that the purpose of the `search` function is to return all lines in
147 `contents` that contain the `query`. Similar to the `filter` example in Listing
148 13-19, this code uses the `filter` adaptor to keep only the lines that
149 `line.contains(query)` returns `true` for. We then collect the matching lines
150 into another vector with `collect`. Much simpler! Feel free to make the same
151 change to use iterator methods in the `search_case_insensitive` function as
152 well.
153
154 The next logical question is which style you should choose in your own code and
155 why: the original implementation in Listing 13-28 or the version using
156 iterators in Listing 13-29. Most Rust programmers prefer to use the iterator
157 style. It’s a bit tougher to get the hang of at first, but once you get a feel
158 for the various iterator adaptors and what they do, iterators can be easier to
159 understand. Instead of fiddling with the various bits of looping and building
160 new vectors, the code focuses on the high-level objective of the loop. This
161 abstracts away some of the commonplace code so it’s easier to see the concepts
162 that are unique to this code, such as the filtering condition each element in
163 the iterator must pass.
164
165 But are the two implementations truly equivalent? The intuitive assumption
166 might be that the more low-level loop will be faster. Let’s talk about
167 performance.