diff --git a/second-edition/src/ch13-03-improving-our-io-project.md b/second-edition/src/ch13-03-improving-our-io-project.md index b10a054ec..3a7202d35 100644 --- a/second-edition/src/ch13-03-improving-our-io-project.md +++ b/second-edition/src/ch13-03-improving-our-io-project.md @@ -3,7 +3,7 @@ In our I/O project implementing `grep` in the last chapter, there are some places where the code could be made clearer and more concise using iterators. Let's take a look at how iterators can improve our implementation of the -`Config::new` function and the `grep` function. +`Config::new` function and the `search` function. ### Removing a `clone` by Using an Iterator @@ -19,11 +19,11 @@ impl Config { return Err("not enough arguments"); } - let search = args[1].clone(); + let query = args[1].clone(); let filename = args[2].clone(); Ok(Config { - search: search, + query: query, filename: filename, }) } @@ -35,7 +35,7 @@ could remove them in the future. Well, that time is now! So, why do we need `clone` here? The issue is that we have a slice with `String` elements in the parameter `args`, and the `new` function does not own `args`. In order to be able to return ownership of a `Config` instance, we need to clone the values -that we put in the `search` and `filename` fields of `Config`, so that the +that we put in the `query` and `filename` fields of `Config`, so that the `Config` instance can own its values. Now that we know more about iterators, we can change the `new` function to @@ -78,7 +78,7 @@ know we can call the `next` method on it! Here's the new code: ```rust # struct Config { -# search: String, +# query: String, # filename: String, # } # @@ -86,9 +86,9 @@ impl Config { fn new(mut args: std::env::Args) -> Result { args.next(); - let search = match args.next() { + let query = match args.next() { Some(arg) => arg, - None => return Err("Didn't get a search string"), + None => return Err("Didn't get a query string"), }; let filename = match args.next() { @@ -97,7 +97,7 @@ impl Config { }; Ok(Config { - search: search, + query: query, filename: filename, }) } @@ -109,13 +109,13 @@ impl Config { Remember that the first value in the return value of `env::args` is the name of the program. We want to ignore that, so first we'll call `next` and not do anything with the return value. The second time we call `next` should be the -value we want to put in the `search` field of `Config`. We use a `match` to +value we want to put in the `query` field of `Config`. We use a `match` to extract the value if `next` returns a `Some`, and we return early with an `Err` value if there weren't enough arguments (which would cause this call to `next` to return `None`). We do the same thing for the `filename` value. It's slightly unfortunate that -the `match` expressions for `search` and `filename` are so similar. It would be +the `match` expressions for `query` and `filename` are so similar. It would be nice if we could use `?` on the `Option` returned from `next`, but `?` only works with `Result` values currently. Even if we could use `?` on `Option` like we can on `Result`, the value we would get would be borrowed, and we want to @@ -124,18 +124,18 @@ move the `String` from the iterator into `Config`. ### Making Code Clearer with Iterator Adaptors The other bit of code where we could take advantage of iterators was in the -`grep` function as implemented in Listing 12-15: +`search` function as implemented in Listing 12-15: ```rust -fn grep<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { +fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { let mut results = Vec::new(); for line in contents.lines() { - if line.contains(search) { + if line.contains(query) { results.push(line); } } @@ -149,30 +149,30 @@ mutable intermediate `results` vector, by using iterator adaptor methods like this instead: ```rust -fn grep<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { +fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { contents.lines() - .filter(|line| line.contains(search)) + .filter(|line| line.contains(query)) .collect() } ``` Here, we use the `filter` adaptor to only keep the lines that -`line.contains(search)` returns true for. We then collect them up into another +`line.contains(query)` returns true for. We then collect them up into another vector with `collect`. Much simpler! -We can use the same technique in the `grep_case_insensitive` function that we +We can use the same technique in the `search_case_insensitive` function that we defined in Listing 12-16 as follows: ```rust -fn grep_case_insensitive<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { - let search = search.to_lowercase(); +fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { + let query = query.to_lowercase(); contents.lines() .filter(|line| { - line.to_lowercase().contains(&search) + line.to_lowercase().contains(&query) }).collect() } ```