Merge main into async-edits

This commit is contained in:
Chris Krycho
2024-07-31 07:11:04 -06:00
16 changed files with 578 additions and 514 deletions

View File

@@ -133,3 +133,13 @@ $ dot dot/trpl04-01.dot -Tsvg > src/img/trpl04-01.svg
In the generated SVG, remove the width and the height attributes from the `svg`
element and set the `viewBox` attribute to `0.00 0.00 1000.00 1000.00` or other
values that don't cut off the image.
## Publish a preview to GitHub Pages
We sometimes publish to GitHub Pages for in-progress previews. The recommended
flow for publishing is:
- Install the `ghp-import` tool by running `pip install ghp-import` (or `pipx install ghp-import`, using [pipx][pipx]).
- In the root, run `tools/generate-preview.sh`
[pipx]: https://pipx.pypa.io/stable/#install-pipx

View File

@@ -75,7 +75,7 @@ executable. Instead, they define functionality intended to be shared with
multiple projects. For example, the `rand` crate we used in Chapter
2 provides functionality that generates random numbers.
Most of the time when Rustaceans say “crate”, they mean library crate, and they
use “crate” interchangeably with the general programming concept of a “library".
use “crate” interchangeably with the general programming concept of a “library.
The *crate root* is a source file that the Rust compiler starts from and makes
up the root module of your crate (well explain modules in depth in the
@@ -139,37 +139,35 @@ in the compiler, and how most developers organize their code. Well be going
through examples of each of these rules throughout this chapter, but this is a
great place to refer to as a reminder of how modules work.
- **Start from the crate root**: When compiling a crate, the compiler first
* **Start from the crate root**: When compiling a crate, the compiler first
looks in the crate root file (usually *src/lib.rs* for a library crate or
*src/main.rs* for a binary crate) for code to compile.
- **Declaring modules**: In the crate root file, you can declare new modules;
say you declare a “garden” module with `mod garden;`. The compiler will look
for the modules code in these places:
- Inline, within curly brackets that replace the semicolon following `mod
garden`
- In the file *src/garden.rs*
- In the file *src/garden/mod.rs*
- **Declaring submodules**: In any file other than the crate root, you can
* **Declaring modules**: In the crate root file, you can declare new modules;
say you declare a “garden” module with `mod garden;`. The compiler will look
for the modules code in these places:
* Inline, within curly brackets that replace the semicolon following `mod garden`
* In the file *src/garden.rs*
* In the file *src/garden/mod.rs*
* **Declaring submodules**: In any file other than the crate root, you can
declare submodules. For example, you might declare `mod vegetables;` in
*src/garden.rs*. The compiler will look for the submodules code within the
directory named for the parent module in these places:
- Inline, directly following `mod vegetables`, within curly brackets instead
* Inline, directly following `mod vegetables`, within curly brackets instead
of the semicolon
- In the file *src/garden/vegetables.rs*
- In the file *src/garden/vegetables/mod.rs*
- **Paths to code in modules**: Once a module is part of your crate, you can
* In the file *src/garden/vegetables.rs*
* In the file *src/garden/vegetables/mod.rs*
* **Paths to code in modules**: Once a module is part of your crate, you can
refer to code in that module from anywhere else in that same crate, as long
as the privacy rules allow, using the path to the code. For example, an
`Asparagus` type in the garden vegetables module would be found at
`crate::garden::vegetables::Asparagus`.
- **Private vs. public**: Code within a module is private from its parent
* **Private vs. public**: Code within a module is private from its parent
modules by default. To make a module public, declare it with `pub mod`
instead of `mod`. To make items within a public module public as well, use
`pub` before their declarations.
- **The `use` keyword**: Within a scope, the `use` keyword creates shortcuts to
* **The `use` keyword**: Within a scope, the `use` keyword creates shortcuts to
items to reduce repetition of long paths. In any scope that can refer to
`crate::garden::vegetables::Asparagus`, you can create a shortcut with `use
crate::garden::vegetables::Asparagus;` and from then on you only need to
`crate::garden::vegetables::Asparagus`, you can create a shortcut with `use crate::garden::vegetables::Asparagus;` and from then on you only need to
write `Asparagus` to make use of that type in the scope.
Here, we create a binary crate named `backyard` that illustrates these rules.
@@ -198,7 +196,7 @@ pub mod garden;
fn main() {
let plant = Asparagus {};
println!("I'm growing {:?}!", plant);
println!("I'm growing {plant:?}!");
}
```
@@ -243,8 +241,7 @@ chefs and cooks work in the kitchen, dishwashers clean up, and managers do
administrative work.
To structure our crate in this way, we can organize its functions into nested
modules. Create a new library named `restaurant` by running `cargo new
restaurant --lib`. Then enter the code in Listing 7-1 into *src/lib.rs* to
modules. Create a new library named `restaurant` by running `cargo new restaurant --lib`. Then enter the code in Listing 7-1 into *src/lib.rs* to
define some modules and function signatures; this code is the front of house
section.
@@ -566,7 +563,7 @@ and `fn add_to_waitlist` lets us call the function from
`eat_at_restaurant`
Now the code will compile! To see why adding the `pub` keyword lets us use
these paths in `add_to_waitlist` with respect to the privacy rules, lets look
these paths in `eat_at_restaurant` with respect to the privacy rules, lets look
at the absolute and the relative paths.
In the absolute path, we start with `crate`, the root of our crates module
@@ -594,26 +591,27 @@ changes to your public API to make it easier for people to depend on your
crate. These considerations are out of the scope of this book; if youre
interested in this topic, see The Rust API Guidelines at *https://rust-lang.github.io/api-guidelines/*.
> #### Best Practices for Packages with a Binary and a Library
>
> We mentioned that a package can contain both a *src/main.rs* binary crate
> root as well as a *src/lib.rs* library crate root, and both crates will have
> the package name by default. Typically, packages with this pattern of
> containing both a library and a binary crate will have just enough code in the
> binary crate to start an executable that calls code within the library crate.
> This lets other projects benefit from most of the functionality that the
> package provides because the library crates code can be shared.
>
> The module tree should be defined in *src/lib.rs*. Then, any public items can
> be used in the binary crate by starting paths with the name of the package.
> The binary crate becomes a user of the library crate just like a completely
> external crate would use the library crate: it can only use the public API.
> This helps you design a good API; not only are you the author, youre also a
> client!
>
> In Chapter 12, well demonstrate this organizational
> practice with a command-line program that will contain both a binary crate
> and a library crate.
>
> #### Best Practices for Packages with a Binary and a Library
>
> We mentioned that a package can contain both a *src/main.rs* binary crate
> root as well as a *src/lib.rs* library crate root, and both crates will have
> the package name by default. Typically, packages with this pattern of
> containing both a library and a binary crate will have just enough code in the
> binary crate to start an executable that calls code within the library crate.
> This lets other projects benefit from most of the functionality that the
> package provides because the library crates code can be shared.
>
> The module tree should be defined in *src/lib.rs*. Then, any public items can
> be used in the binary crate by starting paths with the name of the package.
> The binary crate becomes a user of the library crate just like a completely
> external crate would use the library crate: it can only use the public API.
> This helps you design a good API; not only are you the author, youre also a
> client!
>
> In Chapter 12, well demonstrate this organizational
> practice with a command-line program that will contain both a binary crate
> and a library crate.
### Starting Relative Paths with `super`
@@ -856,8 +854,7 @@ the shortcut in the parent module with `super::hosting` within the child
### Creating Idiomatic `use` Paths
In Listing 7-11, you might have wondered why we specified `use
crate::front_of_house::hosting` and then called `hosting::add_to_waitlist` in
In Listing 7-11, you might have wondered why we specified `use crate::front_of_house::hosting` and then called `hosting::add_to_waitlist` in
`eat_at_restaurant`, rather than specifying the `use` path all the way out to
the `add_to_waitlist` function to achieve the same result, as in Listing 7-13.
@@ -1002,8 +999,7 @@ from a new scope with `pub use`
Before this change, external code would have to call the `add_to_waitlist`
function by using the path
`restaurant::front_of_house::hosting::add_to_waitlist()`, which also would have
required the `front_of_house` module to be marked as `pub`. Now that this `pub
use` has re-exported the `hosting` module from the root module, external code
required the `front_of_house` module to be marked as `pub`. Now that this `pub use` has re-exported the `hosting` module from the root module, external code
can use the path `restaurant::hosting::add_to_waitlist()` instead.
Re-exporting is useful when the internal structure of your code is different
@@ -1246,29 +1242,30 @@ root, and not declared as a child of the `front_of_house` module. The
compilers rules for which files to check for which modules code mean the
directories and files more closely match the module tree.
> ### Alternate File Paths
>
> So far weve covered the most idiomatic file paths the Rust compiler uses,
> but Rust also supports an older style of file path. For a module named
> `front_of_house` declared in the crate root, the compiler will look for the
> modules code in:
>
> * *src/front_of_house.rs* (what we covered)
> * *src/front_of_house/mod.rs* (older style, still supported path)
>
> For a module named `hosting` that is a submodule of `front_of_house`, the
> compiler will look for the modules code in:
>
> * *src/front_of_house/hosting.rs* (what we covered)
> * *src/front_of_house/hosting/mod.rs* (older style, still supported path)
>
> If you use both styles for the same module, youll get a compiler error.
> Using a mix of both styles for different modules in the same project is
> allowed, but might be confusing for people navigating your project.
>
> The main downside to the style that uses files named *mod.rs* is that your
> project can end up with many files named *mod.rs*, which can get confusing
> when you have them open in your editor at the same time.
>
> ### Alternate File Paths
>
> So far weve covered the most idiomatic file paths the Rust compiler uses,
> but Rust also supports an older style of file path. For a module named
> `front_of_house` declared in the crate root, the compiler will look for the
> modules code in:
>
> * *src/front_of_house.rs* (what we covered)
> * *src/front_of_house/mod.rs* (older style, still supported path)
>
> For a module named `hosting` that is a submodule of `front_of_house`, the
> compiler will look for the modules code in:
>
> * *src/front_of_house/hosting.rs* (what we covered)
> * *src/front_of_house/hosting/mod.rs* (older style, still supported path)
>
> If you use both styles for the same module, youll get a compiler error.
> Using a mix of both styles for different modules in the same project is
> allowed, but might be confusing for people navigating your project.
>
> The main downside to the style that uses files named *mod.rs* is that your
> project can end up with many files named *mod.rs*, which can get confusing
> when you have them open in your editor at the same time.
Weve moved each modules code to a separate file, and the module tree remains
the same. The function calls in `eat_at_restaurant` will work without any

File diff suppressed because it is too large Load Diff

View File

@@ -157,7 +157,7 @@ fn rewrite_listing(src: &str, mode: Mode) -> Result<String, String> {
}
ev => state.events.push(Ok(ev)),
};
Ok::<ListingState, String>(state)
Ok::<ListingState<'_>, String>(state)
},
)?;
@@ -190,7 +190,7 @@ struct ListingState<'e> {
impl<'e> ListingState<'e> {
fn open_listing(
&mut self,
tag: pulldown_cmark::CowStr,
tag: pulldown_cmark::CowStr<'_>,
mode: Mode,
) -> Result<(), String> {
// We do not *keep* the version constructed here, just temporarily
@@ -247,7 +247,7 @@ impl<'e> ListingState<'e> {
Ok(())
}
fn close_listing(&mut self, tag: pulldown_cmark::CowStr, mode: Mode) {
fn close_listing(&mut self, tag: pulldown_cmark::CowStr<'_>, mode: Mode) {
let trailing = if !tag.ends_with('>') {
tag.replace("</Listing>", "")
} else {

View File

@@ -46,27 +46,11 @@ fn parse_references(buffer: String) -> (String, HashMap<String, String>) {
fn parse_links((buffer, ref_map): (String, HashMap<String, String>)) -> String {
// FIXME: check which punctuation is allowed by spec.
let re = Regex::new(r###"(?:(?P<pre>(?:```(?:[^`]|`[^`])*`?\n```\n)|(?:[^\[]`[^`\n]+[\n]?[^`\n]*`))|(?:\[(?P<name>[^]]+)\](?:(?:\([[:blank:]]*(?P<val>[^")]*[^ ])(?:[[:blank:]]*"[^"]*")?\))|(?:\[(?P<key>[^]]*)\]))?))"###).expect("could not create regex");
let error_code =
Regex::new(r###"^E\d{4}$"###).expect("could not create regex");
let output = re.replace_all(&buffer, |caps: &Captures<'_>| {
match caps.name("pre") {
Some(pre_section) => pre_section.as_str().to_string(),
None => {
let name = caps.name("name").expect("could not get name").as_str();
// Really we should ignore text inside code blocks,
// this is a hack to not try to treat `#[derive()]`,
// `[profile]`, `[test]`, or `[E\d\d\d\d]` like a link.
if name.starts_with("derive(") ||
name.starts_with("profile") ||
name.starts_with("test") ||
name.starts_with("no_mangle") ||
name.starts_with("cfg") ||
name.starts_with("unoptimized") ||
name.starts_with("ignore") ||
name.starts_with("should_panic") ||
error_code.is_match(name) {
return format!("[{name}]")
}
let val = match caps.name("val") {
// `[name](link)`
@@ -81,8 +65,10 @@ fn parse_links((buffer, ref_map): (String, HashMap<String, String>)) -> String {
_ => ref_map.get(&key.as_str().to_uppercase()).unwrap_or_else(|| panic!("could not find url for the link text `{}`", key.as_str())).to_string(),
}
}
// `[name]` as reference
None => ref_map.get(&name.to_uppercase()).unwrap_or_else(|| panic!("could not find url for the link text `{name}`")).to_string(),
// `[name]` is within code and should not be treated as a link
None => {
return format!("[{name}]");
}
}
}
};
@@ -236,11 +222,11 @@ more text"
}
#[test]
fn parses_link_without_reference_as_reference() {
fn does_not_parse_link_without_reference_as_reference() {
let source = r"[link] is alone
[link]: The contents"
.to_string();
let target = r"link at *The contents* is alone".to_string();
let target = r"[link] is alone".to_string();
assert_eq!(parse(source), target);
}
@@ -294,7 +280,7 @@ version = "0.1.0"
[dependencies]
```
Another [link]
Another [link][]
more text
[link]: http://gohere
"###

View File

@@ -191,7 +191,7 @@ like this:
When we run this program, well see `again!` printed over and over continuously
until we stop the program manually. Most terminals support the keyboard shortcut
<kbd>ctrl</kbd>-<kdb>c</kbd> to interrupt a program that is stuck in a continual
<kbd>ctrl</kbd>-<kbd>c</kbd> to interrupt a program that is stuck in a continual
loop. Give it a try:
<!-- manual-regeneration

View File

@@ -20,7 +20,7 @@ executable. Instead, they define functionality intended to be shared with
multiple projects. For example, the `rand` crate we used in [Chapter
2][rand]<!-- ignore --> provides functionality that generates random numbers.
Most of the time when Rustaceans say “crate”, they mean library crate, and they
use “crate” interchangeably with the general programming concept of a “library".
use “crate” interchangeably with the general programming concept of a “library.
The *crate root* is a source file that the Rust compiler starts from and makes
up the root module of your crate (well explain modules in depth in the
@@ -33,12 +33,9 @@ build those crates. Cargo is actually a package that contains the binary crate
for the command-line tool youve been using to build your code. The Cargo
package also contains a library crate that the binary crate depends on. Other
projects can depend on the Cargo library crate to use the same logic the Cargo
command-line tool uses.
A crate can come in one of two forms: a binary crate or a library crate. A
package can contain as many binary crates as you like, but at most only one
library crate. A package must contain at least one crate, whether thats a
library or binary crate.
command-line tool uses. A package can contain as many binary crates as you
like, but at most only one library crate. A package must contain at least one
crate, whether thats a library or binary crate.
Lets walk through what happens when we create a package. First we enter the
command `cargo new my-project`:

View File

@@ -18,8 +18,8 @@ Along the way, well show how to make our command line tool use the terminal
features that many other command line tools use. Well read the value of an
environment variable to allow the user to configure the behavior of our tool.
Well also print error messages to the standard error console stream (`stderr`)
instead of standard output (`stdout`), so, for example, the user can redirect
successful output to a file while still seeing error messages onscreen.
instead of standard output (`stdout`) so that, for example, the user can
redirect successful output to a file while still seeing error messages onscreen.
One Rust community member, Andrew Gallant, has already created a fully
featured, very fast version of `grep`, called `ripgrep`. By comparison, our
@@ -29,17 +29,15 @@ background knowledge you need to understand a real-world project such as
Our `grep` project will combine a number of concepts youve learned so far:
* Organizing code (using what you learned about modules in [Chapter 7][ch7]<!--
ignore -->)
* Using vectors and strings (collections, [Chapter 8][ch8]<!-- ignore -->)
* Organizing code ([Chapter 7][ch7]<!-- ignore -->)
* Using vectors and strings ([Chapter 8][ch8]<!-- ignore -->)
* Handling errors ([Chapter 9][ch9]<!-- ignore -->)
* Using traits and lifetimes where appropriate ([Chapter 10][ch10]<!-- ignore
-->)
* Using traits and lifetimes where appropriate ([Chapter 10][ch10]<!-- ignore -->)
* Writing tests ([Chapter 11][ch11]<!-- ignore -->)
Well also briefly introduce closures, iterators, and trait objects, which
Chapters [13][ch13]<!-- ignore --> and [17][ch17]<!-- ignore --> will cover in
detail.
[Chapter 13][ch13]<!-- ignore --> and [Chapter 17][ch17]<!-- ignore --> will
cover in detail.
[ch7]: ch07-00-managing-growing-projects-with-packages-crates-and-modules.html
[ch8]: ch08-00-common-collections.html

View File

@@ -37,7 +37,7 @@ to turn it into a collection, such as a vector, that contains all the elements
the iterator produces.
The code in Listing 12-1 allows your `minigrep` program to read any command
line arguments passed to it and then collect the values into a vector.
line arguments passed to it, and then collect the values into a vector.
<Listing number="12-1" file-name="src/main.rs" caption="Collecting the command line arguments into a vector and printing them">
@@ -47,7 +47,7 @@ line arguments passed to it and then collect the values into a vector.
</Listing>
First, we bring the `std::env` module into scope with a `use` statement so we
First we bring the `std::env` module into scope with a `use` statement so we
can use its `args` function. Notice that the `std::env::args` function is
nested in two levels of modules. As we discussed in [Chapter
7][ch7-idiomatic-use]<!-- ignore -->, in cases where the desired function is
@@ -63,14 +63,14 @@ mistaken for a function thats defined in the current module.
> Unicode. If your program needs to accept arguments containing invalid
> Unicode, use `std::env::args_os` instead. That function returns an iterator
> that produces `OsString` values instead of `String` values. Weve chosen to
> use `std::env::args` here for simplicity, because `OsString` values differ
> per platform and are more complex to work with than `String` values.
> use `std::env::args` here for simplicity because `OsString` values differ per
> platform and are more complex to work with than `String` values.
On the first line of `main`, we call `env::args`, and we immediately use
`collect` to turn the iterator into a vector containing all the values produced
by the iterator. We can use the `collect` function to create many kinds of
collections, so we explicitly annotate the type of `args` to specify that we
want a vector of strings. Although we very rarely need to annotate types in
want a vector of strings. Although you very rarely need to annotate types in
Rust, `collect` is one function you do often need to annotate because Rust
isnt able to infer the kind of collection you want.
@@ -89,8 +89,8 @@ Notice that the first value in the vector is `"target/debug/minigrep"`, which
is the name of our binary. This matches the behavior of the arguments list in
C, letting programs use the name by which they were invoked in their execution.
Its often convenient to have access to the program name in case you want to
print it in messages or change behavior of the program based on what command
line alias was used to invoke the program. But for the purposes of this
print it in messages or change the behavior of the program based on what
command line alias was used to invoke the program. But for the purposes of this
chapter, well ignore it and save only the two arguments we need.
### Saving the Argument Values in Variables
@@ -109,7 +109,7 @@ we can use the values throughout the rest of the program. We do that in Listing
</Listing>
As we saw when we printed the vector, the programs name takes up the first
value in the vector at `args[0]`, so were starting arguments at index `1`. The
value in the vector at `args[0]`, so were starting arguments at index 1. The
first argument `minigrep` takes is the string were searching for, so we put a
reference to the first argument in the variable `query`. The second argument
will be the file path, so we put a reference to the second argument in the

View File

@@ -1,13 +1,13 @@
## Reading a File
Now well add functionality to read the file specified in the `file_path`
argument. First, we need a sample file to test it with: well use a file with a
argument. First we need a sample file to test it with: well use a file with a
small amount of text over multiple lines with some repeated words. Listing 12-3
has an Emily Dickinson poem that will work well! Create a file called
*poem.txt* at the root level of your project, and enter the poem “Im Nobody!
Who are you?”
<Listing number="12-3" file-name="poem.txt" caption="A poem by Emily Dickinson makes a good test case">
<Listing number="12-3" file-name="poem.txt" caption="A poem by Emily Dickinson makes a good test case.">
```text
{{#include ../listings/ch12-an-io-project/listing-12-03/poem.txt}}
@@ -26,11 +26,12 @@ shown in Listing 12-4.
</Listing>
First, we bring in a relevant part of the standard library with a `use`
First we bring in a relevant part of the standard library with a `use`
statement: we need `std::fs` to handle files.
In `main`, the new statement `fs::read_to_string` takes the `file_path`, opens
that file, and returns a `std::io::Result<String>` of the files contents.
that file, and returns a value of type `std::io::Result<String>` that contains
the files contents.
After that, we again add a temporary `println!` statement that prints the value
of `contents` after the file is read, so we can check that the program is
@@ -50,6 +51,6 @@ responsibilities: generally, functions are clearer and easier to maintain if
each function is responsible for only one idea. The other problem is that were
not handling errors as well as we could. The program is still small, so these
flaws arent a big problem, but as the program grows, it will be harder to fix
them cleanly. Its good practice to begin refactoring early on when developing
a program, because its much easier to refactor smaller amounts of code. Well
do that next.
them cleanly. Its a good practice to begin refactoring early on when
developing a program because its much easier to refactor smaller amounts of
code. Well do that next.

View File

@@ -41,8 +41,8 @@ community has developed guidelines for splitting the separate concerns of a
binary program when `main` starts getting large. This process has the following
steps:
* Split your program into a *main.rs* and a *lib.rs* and move your programs
logic to *lib.rs*.
* Split your program into a *main.rs* file and a *lib.rs* file and move your
programs logic to *lib.rs*.
* As long as your command line parsing logic is small, it can remain in
*main.rs*.
* When the command line parsing logic starts getting complicated, extract it
@@ -57,7 +57,7 @@ should be limited to the following:
* Handling the error if `run` returns an error
This pattern is about separating concerns: *main.rs* handles running the
program, and *lib.rs* handles all the logic of the task at hand. Because you
program and *lib.rs* handles all the logic of the task at hand. Because you
cant test the `main` function directly, this structure lets you test all of
your programs logic by moving it into functions in *lib.rs*. The code that
remains in *main.rs* will be small enough to verify its correctness by reading
@@ -163,7 +163,7 @@ named for their purpose.
So far, weve extracted the logic responsible for parsing the command line
arguments from `main` and placed it in the `parse_config` function. Doing so
helped us to see that the `query` and `file_path` values were related and that
helped us see that the `query` and `file_path` values were related, and that
relationship should be conveyed in our code. We then added a `Config` struct to
name the related purpose of `query` and `file_path` and to be able to return the
values names as struct field names from the `parse_config` function.
@@ -208,8 +208,8 @@ they should do instead. Lets fix that now.
#### Improving the Error Message
In Listing 12-8, we add a check in the `new` function that will verify that the
slice is long enough before accessing index 1 and 2. If the slice isnt long
enough, the program panics and displays a better error message.
slice is long enough before accessing index 1 and index 2. If the slice isnt
long enough, the program panics and displays a better error message.
<Listing number="12-8" file-name="src/main.rs" caption="Adding a check for the number of arguments">
@@ -222,10 +222,10 @@ enough, the program panics and displays a better error message.
This code is similar to [the `Guess::new` function we wrote in Listing
9-13][ch9-custom-types]<!-- ignore -->, where we called `panic!` when the
`value` argument was out of the range of valid values. Instead of checking for
a range of values here, were checking that the length of `args` is at least 3
and the rest of the function can operate under the assumption that this
a range of values here, were checking that the length of `args` is at least
`3` and the rest of the function can operate under the assumption that this
condition has been met. If `args` has fewer than three items, this condition
will be true, and we call the `panic!` macro to end the program immediately.
will be `true`, and we call the `panic!` macro to end the program immediately.
With these extra few lines of code in `new`, lets run the program without any
arguments again to see what the error looks like now:
@@ -235,8 +235,8 @@ arguments again to see what the error looks like now:
```
This output is better: we now have a reasonable error message. However, we also
have extraneous information we dont want to give to our users. Perhaps using
the technique we used in Listing 9-13 isnt the best to use here: a call to
have extraneous information we dont want to give to our users. Perhaps the
technique we used in Listing 9-13 isnt the best one to use here: a call to
`panic!` is more appropriate for a programming problem than a usage problem,
[as discussed in Chapter 9][ch9-error-guidelines]<!-- ignore -->. Instead,
well use the other technique you learned about in Chapter 9—[returning a
@@ -270,7 +270,7 @@ well, which well do in the next listing.
</Listing>
Our `build` function returns a `Result` with a `Config` instance in the success
case and a `&'static str` in the error case. Our error values will always be
case and a string literal in the error case. Our error values will always be
string literals that have the `'static` lifetime.
Weve made two changes in the body of the function: instead of calling `panic!`
@@ -306,15 +306,15 @@ In this listing, weve used a method we havent covered in detail yet:
`unwrap_or_else`, which is defined on `Result<T, E>` by the standard library.
Using `unwrap_or_else` allows us to define some custom, non-`panic!` error
handling. If the `Result` is an `Ok` value, this methods behavior is similar
to `unwrap`: it returns the inner value `Ok` is wrapping. However, if the value
is an `Err` value, this method calls the code in the *closure*, which is an
anonymous function we define and pass as an argument to `unwrap_or_else`. Well
cover closures in more detail in [Chapter 13][ch13]<!-- ignore -->. For now,
you just need to know that `unwrap_or_else` will pass the inner value of the
`Err`, which in this case is the static string `"not enough arguments"` that we
added in Listing 12-9, to our closure in the argument `err` that appears
between the vertical pipes. The code in the closure can then use the `err`
value when it runs.
to `unwrap`: it returns the inner value that `Ok` is wrapping. However, if the
value is an `Err` value, this method calls the code in the *closure*, which is
an anonymous function we define and pass as an argument to `unwrap_or_else`.
Well cover closures in more detail in [Chapter 13][ch13]<!-- ignore -->. For
now, you just need to know that `unwrap_or_else` will pass the inner value of
the `Err`, which in this case is the static string `"not enough arguments"`
that we added in Listing 12-9, to our closure in the argument `err` that
appears between the vertical pipes. The code in the closure can then use the
`err` value when it runs.
Weve added a new `use` line to bring `process` from the standard library into
scope. The code in the closure that will be run in the error case is only two
@@ -386,7 +386,7 @@ know that `Box<dyn Error>` means the function will return a type that
implements the `Error` trait, but we dont have to specify what particular type
the return value will be. This gives us flexibility to return error values that
may be of different types in different error cases. The `dyn` keyword is short
for dynamic.
for *dynamic*.
Second, weve removed the call to `expect` in favor of the `?` operator, as we
talked about in [Chapter 9][ch9-question-mark]<!-- ignore -->. Rather than
@@ -423,11 +423,11 @@ with `Config::build` in Listing 12-10, but with a slight difference:
```
We use `if let` rather than `unwrap_or_else` to check whether `run` returns an
`Err` value and call `process::exit(1)` if it does. The `run` function doesnt
return a value that we want to `unwrap` in the same way that `Config::build`
returns the `Config` instance. Because `run` returns `()` in the success case,
we only care about detecting an error, so we dont need `unwrap_or_else` to
return the unwrapped value, which would only be `()`.
`Err` value and to call `process::exit(1)` if it does. The `run` function
doesnt return a value that we want to `unwrap` in the same way that
`Config::build` returns the `Config` instance. Because `run` returns `()` in
the success case, we only care about detecting an error, so we dont need
`unwrap_or_else` to return the unwrapped value, which would only be `()`.
The bodies of the `if let` and the `unwrap_or_else` functions are the same in
both cases: we print the error and exit.
@@ -435,10 +435,10 @@ both cases: we print the error and exit.
### Splitting Code into a Library Crate
Our `minigrep` project is looking good so far! Now well split the
*src/main.rs* file and put some code into the *src/lib.rs* file. That way we
*src/main.rs* file and put some code into the *src/lib.rs* file. That way, we
can test the code and have a *src/main.rs* file with fewer responsibilities.
Lets move all the code that isnt the `main` function from *src/main.rs* to
Lets move all the code that isnt in the `main` function from *src/main.rs* to
*src/lib.rs*:
* The `run` function definition
@@ -476,8 +476,7 @@ binary crate in *src/main.rs*, as shown in Listing 12-14.
We add a `use minigrep::Config` line to bring the `Config` type from the
library crate into the binary crates scope, and we prefix the `run` function
with our crate name. Now all the functionality should be connected and should
work. Run the program with `cargo run` and make sure everything works
correctly.
work. Run the program with `cargo run` and make sure everything works correctly.
Whew! That was a lot of work, but weve set ourselves up for success in the
future. Now its much easier to handle errors, and weve made the code more

View File

@@ -6,21 +6,21 @@ for the core functionality of our code. We can call functions directly with
various arguments and check return values without having to call our binary
from the command line.
In this section, well add the searching logic to the `minigrep` program
using the test-driven development (TDD) process with the following steps:
In this section, well add the searching logic to the `minigrep` program using
the test-driven development (TDD) process with the following steps:
1. Write a test that fails and run it to make sure it fails for the reason you
expect.
2. Write or modify just enough code to make the new test pass.
3. Refactor the code you just added or changed and make sure the tests
continue to pass.
3. Refactor the code you just added or changed and make sure the tests continue
to pass.
4. Repeat from step 1!
Though its just one of many ways to write software, TDD can help drive code
design. Writing the test before you write the code that makes the test pass
helps to maintain high test coverage throughout the process.
Well test drive the implementation of the functionality that will actually do
Well test-drive the implementation of the functionality that will actually do
the searching for the query string in the file contents and produce a list of
lines that match the query. Well add this functionality in a function called
`search`.
@@ -29,11 +29,11 @@ lines that match the query. Well add this functionality in a function called
Because we dont need them anymore, lets remove the `println!` statements from
*src/lib.rs* and *src/main.rs* that we used to check the programs behavior.
Then, in *src/lib.rs*, add a `tests` module with a test function, as we did in
[Chapter 11][ch11-anatomy]<!-- ignore -->. The test function specifies the
behavior we want the `search` function to have: it will take a query and the
text to search, and it will return only the lines from the text that contain
the query. Listing 12-15 shows this test, which wont compile yet.
Then, in *src/lib.rs*, well add a `tests` module with a test function, as we
did in [Chapter 11][ch11-anatomy]<!-- ignore -->. The test function specifies
the behavior we want the `search` function to have: it will take a query and
the text to search, and it will return only the lines from the text that
contain the query. Listing 12-15 shows this test, which wont compile yet.
<Listing number="12-15" file-name="src/lib.rs" caption="Creating a failing test for the `search` function we wish we had">
@@ -44,7 +44,7 @@ the query. Listing 12-15 shows this test, which wont compile yet.
</Listing>
This test searches for the string `"duct"`. The text were searching is three
lines, only one of which contains `"duct"` (Note that the backslash after the
lines, only one of which contains `"duct"` (note that the backslash after the
opening double quote tells Rust not to put a newline character at the beginning
of the contents of this string literal). We assert that the value returned from
the `search` function contains only the line we expect.
@@ -95,9 +95,9 @@ syntax.
Other programming languages dont require you to connect arguments to return
values in the signature, but this practice will get easier over time. You might
want to compare this example with the [“Validating References with
Lifetimes”][validating-references-with-lifetimes]<!-- ignore --> section in
Chapter 10.
want to compare this example with the examples in the [“Validating References
with Lifetimes”][validating-references-with-lifetimes]<!-- ignore --> section
in Chapter 10.
Now lets run the test:
@@ -112,19 +112,19 @@ Great, the test fails, exactly as we expected. Lets get the test to pass!
Currently, our test is failing because we always return an empty vector. To fix
that and implement `search`, our program needs to follow these steps:
* Iterate through each line of the contents.
* Check whether the line contains our query string.
* If it does, add it to the list of values were returning.
* If it doesnt, do nothing.
* Return the list of results that match.
1. Iterate through each line of the contents.
2. Check whether the line contains our query string.
3. If it does, add it to the list of values were returning.
4. If it doesnt, do nothing.
5. Return the list of results that match.
Lets work through each step, starting with iterating through lines.
#### Iterating Through Lines with the `lines` Method
Rust has a helpful method to handle line-by-line iteration of strings,
conveniently named `lines`, that works as shown in Listing 12-17. Note this
wont compile yet.
conveniently named `lines`, that works as shown in Listing 12-17. Note that
this wont compile yet.
<Listing number="12-17" file-name="src/lib.rs" caption="Iterating through each line in `contents`">
@@ -144,7 +144,7 @@ of using an iterator in [Listing 3-5][ch3-iter]<!-- ignore -->, where we used a
Next, well check whether the current line contains our query string.
Fortunately, strings have a helpful method named `contains` that does this for
us! Add a call to the `contains` method in the `search` function, as shown in
Listing 12-18. Note this still wont compile yet.
Listing 12-18. Note that this still wont compile yet.
<Listing number="12-18" file-name="src/lib.rs" caption="Adding functionality to see whether the line contains the string in `query`">
@@ -154,8 +154,8 @@ Listing 12-18. Note this still wont compile yet.
</Listing>
At the moment, were building up functionality. To get it to compile, we need
to return a value from the body as we indicated we would in the function
At the moment, were building up functionality. To get the code to compile, we
need to return a value from the body as we indicated we would in the function
signature.
#### Storing Matching Lines
@@ -205,20 +205,20 @@ will print each line returned from `search`:
Were still using a `for` loop to return each line from `search` and print it.
Now the entire program should work! Lets try it out, first with a word that
should return exactly one line from the Emily Dickinson poem, “frog”:
should return exactly one line from the Emily Dickinson poem: *frog*.
```console
{{#include ../listings/ch12-an-io-project/no-listing-02-using-search-in-run/output.txt}}
```
Cool! Now lets try a word that will match multiple lines, like body:
Cool! Now lets try a word that will match multiple lines, like *body*:
```console
{{#include ../listings/ch12-an-io-project/output-only-03-multiple-matches/output.txt}}
```
And finally, lets make sure that we dont get any lines when we search for a
word that isnt anywhere in the poem, such as monomorphization:
word that isnt anywhere in the poem, such as *monomorphization*:
```console
{{#include ../listings/ch12-an-io-project/output-only-04-no-matches/output.txt}}

View File

@@ -25,7 +25,7 @@ tests, as shown in Listing 12-20.
</Listing>
Note that weve edited the old tests `contents` too. Weve added a new line
with the text `"Duct tape."` using a capital D that shouldnt match the query
with the text `"Duct tape."` using a capital *D* that shouldnt match the query
`"duct"` when were searching in a case-sensitive manner. Changing the old test
in this way helps ensure that we dont accidentally break the case-sensitive
search functionality that weve already implemented. This test should pass now
@@ -33,9 +33,9 @@ and should continue to pass as we work on the case-insensitive search.
The new test for the case-*insensitive* search uses `"rUsT"` as its query. In
the `search_case_insensitive` function were about to add, the query `"rUsT"`
should match the line containing `"Rust:"` with a capital R and match the line
`"Trust me."` even though both have different casing from the query. This is
our failing test, and it will fail to compile because we havent yet defined
should match the line containing `"Rust:"` with a capital *R* and match the
line `"Trust me."` even though both have different casing from the query. This
is our failing test, and it will fail to compile because we havent yet defined
the `search_case_insensitive` function. Feel free to add a skeleton
implementation that always returns an empty vector, similar to the way we did
for the `search` function in Listing 12-16 to see the test compile and fail.
@@ -44,7 +44,7 @@ for the `search` function in Listing 12-16 to see the test compile and fail.
The `search_case_insensitive` function, shown in Listing 12-21, will be almost
the same as the `search` function. The only difference is that well lowercase
the `query` and each `line` so whatever the case of the input arguments,
the `query` and each `line` so that whatever the case of the input arguments,
theyll be the same case when we check whether the line contains the query.
<Listing number="12-21" file-name="src/lib.rs" caption="Defining the `search_case_insensitive` function to lowercase the query and the line before comparing them">
@@ -55,8 +55,8 @@ theyll be the same case when we check whether the line contains the query.
</Listing>
First, we lowercase the `query` string and store it in a shadowed variable with
the same name. Calling `to_lowercase` on the query is necessary so no
First we lowercase the `query` string and store it in a shadowed variable with
the same name. Calling `to_lowercase` on the query is necessary so that no
matter whether the users query is `"rust"`, `"RUST"`, `"Rust"`, or `"rUsT"`,
well treat the query as if it were `"rust"` and be insensitive to the case.
While `to_lowercase` will handle basic Unicode, it wont be 100% accurate. If
@@ -64,7 +64,7 @@ we were writing a real application, wed want to do a bit more work here, but
this section is about environment variables, not Unicode, so well leave it at
that here.
Note that `query` is now a `String` rather than a string slice, because calling
Note that `query` is now a `String` rather than a string slice because calling
`to_lowercase` creates new data rather than referencing existing data. Say the
query is `"rUsT"`, as an example: that string slice doesnt contain a lowercase
`u` or `t` for us to use, so we have to allocate a new `String` containing
@@ -83,10 +83,10 @@ Lets see if this implementation passes the tests:
```
Great! They passed. Now, lets call the new `search_case_insensitive` function
from the `run` function. First, well add a configuration option to the
`Config` struct to switch between case-sensitive and case-insensitive search.
Adding this field will cause compiler errors because we arent initializing
this field anywhere yet:
from the `run` function. First well add a configuration option to the `Config`
struct to switch between case-sensitive and case-insensitive search. Adding
this field will cause compiler errors because we arent initializing this field
anywhere yet:
<span class="filename">Filename: src/lib.rs</span>
@@ -110,7 +110,7 @@ function, as shown in Listing 12-22. This still wont compile yet.
Finally, we need to check for the environment variable. The functions for
working with environment variables are in the `env` module in the standard
library, so we bring that module into scope at the top of *src/lib.rs*. Then
well use the `var` function from the `env` module to check if any value
well use the `var` function from the `env` module to check to see if any value
has been set for an environment variable named `IGNORE_CASE`, as shown in
Listing 12-23.
@@ -122,7 +122,7 @@ Listing 12-23.
</Listing>
Here, we create a new variable `ignore_case`. To set its value, we call the
Here, we create a new variable, `ignore_case`. To set its value, we call the
`env::var` function and pass it the name of the `IGNORE_CASE` environment
variable. The `env::var` function returns a `Result` that will be the
successful `Ok` variant that contains the value of the environment variable if
@@ -132,7 +132,7 @@ if the environment variable is not set.
Were using the `is_ok` method on the `Result` to check whether the environment
variable is set, which means the program should do a case-insensitive search.
If the `IGNORE_CASE` environment variable isnt set to anything, `is_ok` will
return false and the program will perform a case-sensitive search. We dont
return `false` and the program will perform a case-sensitive search. We dont
care about the *value* of the environment variable, just whether its set or
unset, so were checking `is_ok` rather than using `unwrap`, `expect`, or any
of the other methods weve seen on `Result`.
@@ -141,16 +141,16 @@ We pass the value in the `ignore_case` variable to the `Config` instance so the
`run` function can read that value and decide whether to call
`search_case_insensitive` or `search`, as we implemented in Listing 12-22.
Lets give it a try! First, well run our program without the environment
Lets give it a try! First well run our program without the environment
variable set and with the query `to`, which should match any line that contains
the word to in all lowercase:
the word *to* in all lowercase:
```console
{{#include ../listings/ch12-an-io-project/listing-12-23/output.txt}}
```
Looks like that still works! Now, lets run the program with `IGNORE_CASE`
set to `1` but with the same query `to`.
Looks like that still works! Now lets run the program with `IGNORE_CASE` set
to `1` but with the same query *to*:
```console
$ IGNORE_CASE=1 cargo run -- to poem.txt
@@ -163,14 +163,14 @@ run the program as separate commands:
PS> $Env:IGNORE_CASE=1; cargo run -- to poem.txt
```
This will make `IGNORE_CASE` persist for the remainder of your shell
session. It can be unset with the `Remove-Item` cmdlet:
This will make `IGNORE_CASE` persist for the remainder of your shell session.
It can be unset with the `Remove-Item` cmdlet:
```console
PS> Remove-Item Env:IGNORE_CASE
```
We should get lines that contain to that might have uppercase letters:
We should get lines that contain *to* that might have uppercase letters:
<!-- manual-regeneration
cd listings/ch12-an-io-project/listing-12-23
@@ -185,7 +185,7 @@ To tell your name the livelong day
To an admiring bog!
```
Excellent, we also got lines containing To! Our `minigrep` program can now do
Excellent, we also got lines containing *To*! Our `minigrep` program can now do
case-insensitive searching controlled by an environment variable. Now you know
how to manage options set using either command line arguments or environment
variables.

View File

@@ -7,12 +7,12 @@ error messages. This distinction enables users to choose to direct the
successful output of a program to a file but still print error messages to the
screen.
The `println!` macro is only capable of printing to standard output, so we
have to use something else to print to standard error.
The `println!` macro is only capable of printing to standard output, so we have
to use something else to print to standard error.
### Checking Where Errors Are Written
First, lets observe how the content printed by `minigrep` is currently being
First lets observe how the content printed by `minigrep` is currently being
written to standard output, including any error messages we want to write to
standard error instead. Well do that by redirecting the standard output stream
to a file while intentionally causing an error. We wont redirect the standard
@@ -21,7 +21,7 @@ the screen.
Command line programs are expected to send error messages to the standard error
stream so we can still see error messages on the screen even if we redirect the
standard output stream to a file. Our program is not currently well-behaved:
standard output stream to a file. Our program is not currently well behaved:
were about to see that it saves the error message output to a file instead!
To demonstrate this behavior, well run the program with `>` and the file path,
@@ -105,3 +105,4 @@ well tested.
Next, well explore some Rust features that were influenced by functional
languages: closures and iterators.

6
tools/generate-preview.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
mdbook build
cp ./tools/preview-robots.txt ./book/robots.txt
ghp-import -m "rebuild GitHub Pages from generated-book" book
git push origin gh-pages

2
tools/preview-robots.txt Normal file
View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow: /