mirror of
https://github.com/rust-lang/book.git
synced 2026-05-16 05:41:15 -04:00
Merge main into async-edits
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 (we’ll explain modules in depth in the
|
||||
@@ -139,37 +139,35 @@ in the compiler, and how most developers organize their code. We’ll 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 module’s 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 module’s 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 submodule’s 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, let’s look
|
||||
these paths in `eat_at_restaurant` with respect to the privacy rules, let’s look
|
||||
at the absolute and the relative paths.
|
||||
|
||||
In the absolute path, we start with `crate`, the root of our crate’s 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 you’re
|
||||
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 crate’s 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, you’re also a
|
||||
> client!
|
||||
>
|
||||
> In Chapter 12, we’ll 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 crate’s 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, you’re also a
|
||||
> client!
|
||||
>
|
||||
> In Chapter 12, we’ll 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
|
||||
compiler’s 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 we’ve 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
|
||||
> module’s 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 module’s 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, you’ll 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 we’ve 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
|
||||
> module’s 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 module’s 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, you’ll 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.
|
||||
|
||||
We’ve moved each module’s 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
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
"###
|
||||
|
||||
@@ -191,7 +191,7 @@ like this:
|
||||
|
||||
When we run this program, we’ll 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
|
||||
|
||||
@@ -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 (we’ll 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 you’ve 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 that’s 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 that’s a library or binary crate.
|
||||
|
||||
Let’s walk through what happens when we create a package. First we enter the
|
||||
command `cargo new my-project`:
|
||||
|
||||
@@ -18,8 +18,8 @@ Along the way, we’ll show how to make our command line tool use the terminal
|
||||
features that many other command line tools use. We’ll read the value of an
|
||||
environment variable to allow the user to configure the behavior of our tool.
|
||||
We’ll 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 you’ve 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 -->)
|
||||
|
||||
We’ll 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
|
||||
|
||||
@@ -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 that’s 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. We’ve 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
|
||||
isn’t 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.
|
||||
It’s 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, we’ll 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 program’s name takes up the first
|
||||
value in the vector at `args[0]`, so we’re starting arguments at index `1`. The
|
||||
value in the vector at `args[0]`, so we’re starting arguments at index 1. The
|
||||
first argument `minigrep` takes is the string we’re 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
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
## Reading a File
|
||||
|
||||
Now we’ll add functionality to read the file specified in the `file_path`
|
||||
argument. First, we need a sample file to test it with: we’ll use a file with a
|
||||
argument. First we need a sample file to test it with: we’ll 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 “I’m 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 file’s contents.
|
||||
that file, and returns a value of type `std::io::Result<String>` that contains
|
||||
the file’s 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 we’re
|
||||
not handling errors as well as we could. The program is still small, so these
|
||||
flaws aren’t a big problem, but as the program grows, it will be harder to fix
|
||||
them cleanly. It’s good practice to begin refactoring early on when developing
|
||||
a program, because it’s much easier to refactor smaller amounts of code. We’ll
|
||||
do that next.
|
||||
them cleanly. It’s a good practice to begin refactoring early on when
|
||||
developing a program because it’s much easier to refactor smaller amounts of
|
||||
code. We’ll do that next.
|
||||
|
||||
@@ -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 program’s
|
||||
logic to *lib.rs*.
|
||||
* Split your program into a *main.rs* file and a *lib.rs* file and move your
|
||||
program’s 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
|
||||
can’t test the `main` function directly, this structure lets you test all of
|
||||
your program’s 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, we’ve 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. Let’s 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 isn’t 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 isn’t
|
||||
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, we’re 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, we’re 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`, let’s 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 don’t want to give to our users. Perhaps using
|
||||
the technique we used in Listing 9-13 isn’t the best to use here: a call to
|
||||
have extraneous information we don’t want to give to our users. Perhaps the
|
||||
technique we used in Listing 9-13 isn’t 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,
|
||||
we’ll use the other technique you learned about in Chapter 9—[returning a
|
||||
@@ -270,7 +270,7 @@ well, which we’ll 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.
|
||||
|
||||
We’ve made two changes in the body of the function: instead of calling `panic!`
|
||||
@@ -306,15 +306,15 @@ In this listing, we’ve used a method we haven’t 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 method’s 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`. We’ll
|
||||
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`.
|
||||
We’ll 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.
|
||||
|
||||
We’ve 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 don’t 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, we’ve 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 doesn’t
|
||||
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 don’t 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
|
||||
doesn’t 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 don’t 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 we’ll 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.
|
||||
|
||||
Let’s move all the code that isn’t the `main` function from *src/main.rs* to
|
||||
Let’s move all the code that isn’t 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 crate’s 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 we’ve set ourselves up for success in the
|
||||
future. Now it’s much easier to handle errors, and we’ve made the code more
|
||||
|
||||
@@ -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, we’ll add the searching logic to the `minigrep` program
|
||||
using the test-driven development (TDD) process with the following steps:
|
||||
In this section, we’ll 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 it’s 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.
|
||||
|
||||
We’ll test drive the implementation of the functionality that will actually do
|
||||
We’ll 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. We’ll add this functionality in a function called
|
||||
`search`.
|
||||
@@ -29,11 +29,11 @@ lines that match the query. We’ll add this functionality in a function called
|
||||
|
||||
Because we don’t need them anymore, let’s remove the `println!` statements from
|
||||
*src/lib.rs* and *src/main.rs* that we used to check the program’s 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 won’t compile yet.
|
||||
Then, in *src/lib.rs*, we’ll 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 won’t 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 won’t compile yet.
|
||||
</Listing>
|
||||
|
||||
This test searches for the string `"duct"`. The text we’re 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 don’t 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 let’s run the test:
|
||||
|
||||
@@ -112,19 +112,19 @@ Great, the test fails, exactly as we expected. Let’s 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 we’re returning.
|
||||
* If it doesn’t, 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 we’re returning.
|
||||
4. If it doesn’t, do nothing.
|
||||
5. Return the list of results that match.
|
||||
|
||||
Let’s 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
|
||||
won’t compile yet.
|
||||
conveniently named `lines`, that works as shown in Listing 12-17. Note that
|
||||
this won’t 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, we’ll 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 won’t compile yet.
|
||||
Listing 12-18. Note that this still won’t 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 won’t compile yet.
|
||||
|
||||
</Listing>
|
||||
|
||||
At the moment, we’re 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, we’re 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`:
|
||||
We’re still using a `for` loop to return each line from `search` and print it.
|
||||
|
||||
Now the entire program should work! Let’s 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 let’s try a word that will match multiple lines, like “body”:
|
||||
Cool! Now let’s 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, let’s make sure that we don’t get any lines when we search for a
|
||||
word that isn’t anywhere in the poem, such as “monomorphization”:
|
||||
word that isn’t anywhere in the poem, such as *monomorphization*:
|
||||
|
||||
```console
|
||||
{{#include ../listings/ch12-an-io-project/output-only-04-no-matches/output.txt}}
|
||||
|
||||
@@ -25,7 +25,7 @@ tests, as shown in Listing 12-20.
|
||||
</Listing>
|
||||
|
||||
Note that we’ve edited the old test’s `contents` too. We’ve added a new line
|
||||
with the text `"Duct tape."` using a capital D that shouldn’t match the query
|
||||
with the text `"Duct tape."` using a capital *D* that shouldn’t match the query
|
||||
`"duct"` when we’re searching in a case-sensitive manner. Changing the old test
|
||||
in this way helps ensure that we don’t accidentally break the case-sensitive
|
||||
search functionality that we’ve 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 we’re 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 haven’t 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 haven’t 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 we’ll 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,
|
||||
they’ll 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 @@ they’ll 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 user’s query is `"rust"`, `"RUST"`, `"Rust"`, or `"rUsT"`,
|
||||
we’ll treat the query as if it were `"rust"` and be insensitive to the case.
|
||||
While `to_lowercase` will handle basic Unicode, it won’t be 100% accurate. If
|
||||
@@ -64,7 +64,7 @@ we were writing a real application, we’d want to do a bit more work here, but
|
||||
this section is about environment variables, not Unicode, so we’ll 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 doesn’t contain a lowercase
|
||||
`u` or `t` for us to use, so we have to allocate a new `String` containing
|
||||
@@ -83,10 +83,10 @@ Let’s see if this implementation passes the tests:
|
||||
```
|
||||
|
||||
Great! They passed. Now, let’s call the new `search_case_insensitive` function
|
||||
from the `run` function. First, we’ll 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 aren’t initializing
|
||||
this field anywhere yet:
|
||||
from the `run` function. First we’ll 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 aren’t 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 won’t 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
|
||||
we’ll use the `var` function from the `env` module to check if any value
|
||||
we’ll 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.
|
||||
We’re 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 isn’t set to anything, `is_ok` will
|
||||
return false and the program will perform a case-sensitive search. We don’t
|
||||
return `false` and the program will perform a case-sensitive search. We don’t
|
||||
care about the *value* of the environment variable, just whether it’s set or
|
||||
unset, so we’re checking `is_ok` rather than using `unwrap`, `expect`, or any
|
||||
of the other methods we’ve 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.
|
||||
|
||||
Let’s give it a try! First, we’ll run our program without the environment
|
||||
Let’s give it a try! First we’ll 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, let’s run the program with `IGNORE_CASE`
|
||||
set to `1` but with the same query `to`.
|
||||
Looks like that still works! Now let’s 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.
|
||||
|
||||
@@ -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, let’s observe how the content printed by `minigrep` is currently being
|
||||
First let’s 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. We’ll do that by redirecting the standard output stream
|
||||
to a file while intentionally causing an error. We won’t 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:
|
||||
we’re about to see that it saves the error message output to a file instead!
|
||||
|
||||
To demonstrate this behavior, we’ll run the program with `>` and the file path,
|
||||
@@ -105,3 +105,4 @@ well tested.
|
||||
|
||||
Next, we’ll explore some Rust features that were influenced by functional
|
||||
languages: closures and iterators.
|
||||
|
||||
|
||||
6
tools/generate-preview.sh
Executable file
6
tools/generate-preview.sh
Executable 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
2
tools/preview-robots.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow: /
|
||||
Reference in New Issue
Block a user