diff --git a/ci/dictionary.txt b/ci/dictionary.txt index 5807c2b283..8b7db48fe1 100644 --- a/ci/dictionary.txt +++ b/ci/dictionary.txt @@ -205,6 +205,7 @@ grapheme Grapheme growable gzip +handcoded handoff hardcode hardcoded diff --git a/listings/ch03-common-programming-concepts/no-listing-24-comments-end-of-line/src/main.rs b/listings/ch03-common-programming-concepts/no-listing-24-comments-end-of-line/src/main.rs index 535f4b993b..64b55dbbf8 100644 --- a/listings/ch03-common-programming-concepts/no-listing-24-comments-end-of-line/src/main.rs +++ b/listings/ch03-common-programming-concepts/no-listing-24-comments-end-of-line/src/main.rs @@ -1,3 +1,3 @@ fn main() { - let lucky_number = 7; // I’m feeling lucky today + let lucky_number = 7; // I'm feeling lucky today } diff --git a/listings/ch03-common-programming-concepts/no-listing-25-comments-above-line/src/main.rs b/listings/ch03-common-programming-concepts/no-listing-25-comments-above-line/src/main.rs index 81cd935591..2d619c2bee 100644 --- a/listings/ch03-common-programming-concepts/no-listing-25-comments-above-line/src/main.rs +++ b/listings/ch03-common-programming-concepts/no-listing-25-comments-above-line/src/main.rs @@ -1,4 +1,4 @@ fn main() { - // I’m feeling lucky today + // I'm feeling lucky today let lucky_number = 7; } diff --git a/listings/ch04-understanding-ownership/listing-04-04/src/main.rs b/listings/ch04-understanding-ownership/listing-04-04/src/main.rs index e206bec7a8..c18dc5bc90 100644 --- a/listings/ch04-understanding-ownership/listing-04-04/src/main.rs +++ b/listings/ch04-understanding-ownership/listing-04-04/src/main.rs @@ -1,29 +1,30 @@ fn main() { - let s1 = gives_ownership(); // gives_ownership moves its return - // value into s1 + let s1 = gives_ownership(); // gives_ownership moves its return + // value into s1 - let s2 = String::from("hello"); // s2 comes into scope + let s2 = String::from("hello"); // s2 comes into scope - let s3 = takes_and_gives_back(s2); // s2 is moved into - // takes_and_gives_back, which also - // moves its return value into s3 + let s3 = takes_and_gives_back(s2); // s2 is moved into + // takes_and_gives_back, which also + // moves its return value into s3 } // Here, s3 goes out of scope and is dropped. s2 was moved, so nothing // happens. s1 goes out of scope and is dropped. -fn gives_ownership() -> String { // gives_ownership will move its - // return value into the function - // that calls it +fn gives_ownership() -> String { // gives_ownership will move its + // return value into the function + // that calls it let some_string = String::from("yours"); // some_string comes into scope - some_string // some_string is returned and - // moves out to the calling - // function + some_string // some_string is returned and + // moves out to the calling + // function } -// This function takes a String and returns one -fn takes_and_gives_back(a_string: String) -> String { // a_string comes into - // scope +// This function takes a String and returns a String. +fn takes_and_gives_back(a_string: String) -> String { + // a_string comes into + // scope a_string // a_string is returned and moves out to the calling function } diff --git a/listings/ch04-understanding-ownership/listing-04-09/src/main.rs b/listings/ch04-understanding-ownership/listing-04-09/src/main.rs index 5a6ceaa1e4..161b388544 100644 --- a/listings/ch04-understanding-ownership/listing-04-09/src/main.rs +++ b/listings/ch04-understanding-ownership/listing-04-09/src/main.rs @@ -16,16 +16,17 @@ fn first_word(s: &str) -> &str { fn main() { let my_string = String::from("hello world"); - // `first_word` works on slices of `String`s, whether partial or whole + // `first_word` works on slices of `String`s, whether partial or whole. let word = first_word(&my_string[0..6]); let word = first_word(&my_string[..]); // `first_word` also works on references to `String`s, which are equivalent - // to whole slices of `String`s + // to whole slices of `String`s. let word = first_word(&my_string); let my_string_literal = "hello world"; - // `first_word` works on slices of string literals, whether partial or whole + // `first_word` works on slices of string literals, whether partial or + // whole. let word = first_word(&my_string_literal[0..6]); let word = first_word(&my_string_literal[..]); diff --git a/listings/ch04-understanding-ownership/no-listing-13-reference-scope-ends/src/main.rs b/listings/ch04-understanding-ownership/no-listing-13-reference-scope-ends/src/main.rs index c005414d8e..3034e12de4 100644 --- a/listings/ch04-understanding-ownership/no-listing-13-reference-scope-ends/src/main.rs +++ b/listings/ch04-understanding-ownership/no-listing-13-reference-scope-ends/src/main.rs @@ -5,7 +5,7 @@ fn main() { let r1 = &s; // no problem let r2 = &s; // no problem println!("{r1} and {r2}"); - // variables r1 and r2 will not be used after this point + // Variables r1 and r2 will not be used after this point. let r3 = &mut s; // no problem println!("{r3}"); diff --git a/listings/ch04-understanding-ownership/no-listing-15-dangling-reference-annotated/src/main.rs b/listings/ch04-understanding-ownership/no-listing-15-dangling-reference-annotated/src/main.rs index 9fbb372a05..2a0b0d78c0 100644 --- a/listings/ch04-understanding-ownership/no-listing-15-dangling-reference-annotated/src/main.rs +++ b/listings/ch04-understanding-ownership/no-listing-15-dangling-reference-annotated/src/main.rs @@ -8,6 +8,6 @@ fn dangle() -> &String { // dangle returns a reference to a String let s = String::from("hello"); // s is a new String &s // we return a reference to the String, s -} // Here, s goes out of scope, and is dropped. Its memory goes away. +} // Here, s goes out of scope, and is dropped, so its memory goes away. // Danger! -// ANCHOR_END: here + // ANCHOR_END: here diff --git a/listings/ch07-managing-growing-projects/listing-07-05/src/lib.rs b/listings/ch07-managing-growing-projects/listing-07-05/src/lib.rs index 05372dbe5e..a6808159df 100644 --- a/listings/ch07-managing-growing-projects/listing-07-05/src/lib.rs +++ b/listings/ch07-managing-growing-projects/listing-07-05/src/lib.rs @@ -1,9 +1,12 @@ +// ANCHOR: here mod front_of_house { pub mod hosting { fn add_to_waitlist() {} } } +// -- snip -- +// ANCHOR_END: here pub fn eat_at_restaurant() { // Absolute path crate::front_of_house::hosting::add_to_waitlist(); diff --git a/listings/ch07-managing-growing-projects/listing-07-07/src/lib.rs b/listings/ch07-managing-growing-projects/listing-07-07/src/lib.rs index 7b89ee7cd2..af5ae77f5f 100644 --- a/listings/ch07-managing-growing-projects/listing-07-07/src/lib.rs +++ b/listings/ch07-managing-growing-projects/listing-07-07/src/lib.rs @@ -1,9 +1,12 @@ +// ANCHOR: here mod front_of_house { pub mod hosting { pub fn add_to_waitlist() {} } } +// -- snip -- +// ANCHOR_END: here pub fn eat_at_restaurant() { // Absolute path crate::front_of_house::hosting::add_to_waitlist(); diff --git a/listings/ch07-managing-growing-projects/listing-07-09/src/lib.rs b/listings/ch07-managing-growing-projects/listing-07-09/src/lib.rs index 92c4695d50..48eb996a8b 100644 --- a/listings/ch07-managing-growing-projects/listing-07-09/src/lib.rs +++ b/listings/ch07-managing-growing-projects/listing-07-09/src/lib.rs @@ -15,13 +15,13 @@ mod back_of_house { } pub fn eat_at_restaurant() { - // Order a breakfast in the summer with Rye toast + // Order a breakfast in the summer with Rye toast. let mut meal = back_of_house::Breakfast::summer("Rye"); - // Change our mind about what bread we'd like + // Change our mind about what bread we'd like. meal.toast = String::from("Wheat"); println!("I'd like {} toast please", meal.toast); // The next line won't compile if we uncomment it; we're not allowed - // to see or modify the seasonal fruit that comes with the meal + // to see or modify the seasonal fruit that comes with the meal. // meal.seasonal_fruit = String::from("blueberries"); } diff --git a/listings/ch08-common-collections/listing-08-12/src/main.rs b/listings/ch08-common-collections/listing-08-12/src/main.rs index d9e5e768ab..48f00ef3cb 100644 --- a/listings/ch08-common-collections/listing-08-12/src/main.rs +++ b/listings/ch08-common-collections/listing-08-12/src/main.rs @@ -4,7 +4,7 @@ fn main() { let s = data.to_string(); - // the method also works on a literal directly: + // The method also works on a literal directly: let s = "initial contents".to_string(); // ANCHOR_END: here } diff --git a/listings/ch15-smart-pointers/listing-15-26/src/main.rs b/listings/ch15-smart-pointers/listing-15-26/src/main.rs index 08963aaa51..1c811968fb 100644 --- a/listings/ch15-smart-pointers/listing-15-26/src/main.rs +++ b/listings/ch15-smart-pointers/listing-15-26/src/main.rs @@ -38,7 +38,7 @@ fn main() { println!("a rc count after changing a = {}", Rc::strong_count(&a)); // Uncomment the next line to see that we have a cycle; - // it will overflow the stack + // it will overflow the stack. // println!("a next item = {:?}", a.tail()); } // ANCHOR_END: here diff --git a/listings/ch20-advanced-features/listing-20-32/hello_macro/hello_macro_derive/src/lib.rs b/listings/ch20-advanced-features/listing-20-32/hello_macro/hello_macro_derive/src/lib.rs index 839ec83593..7ae9e55df3 100644 --- a/listings/ch20-advanced-features/listing-20-32/hello_macro/hello_macro_derive/src/lib.rs +++ b/listings/ch20-advanced-features/listing-20-32/hello_macro/hello_macro_derive/src/lib.rs @@ -4,9 +4,9 @@ use quote::quote; #[proc_macro_derive(HelloMacro)] pub fn hello_macro_derive(input: TokenStream) -> TokenStream { // Construct a representation of Rust code as a syntax tree - // that we can manipulate + // that we can manipulate. let ast = syn::parse(input).unwrap(); - // Build the trait implementation + // Build the trait implementation. impl_hello_macro(&ast) } diff --git a/src/appendix-03-derivable-traits.md b/src/appendix-03-derivable-traits.md index c1839b276f..5cffa46cf8 100644 --- a/src/appendix-03-derivable-traits.md +++ b/src/appendix-03-derivable-traits.md @@ -83,9 +83,9 @@ that also implement `PartialEq`. Deriving `PartialOrd` implements the `partial_cmp` method, which returns an `Option` that will be `None` when the values given don’t produce an ordering. An example of a value that doesn’t produce an ordering, even though -most values of that type can be compared, is the not-a-number (`NaN`) floating -point value. Calling `partial_cmp` with any floating point number and the `NaN` -floating point value will return `None`. +most values of that type can be compared, is the `NaN` floating point value. +Calling `partial_cmp` with any floating point number and the `NaN` floating +point value will return `None`. When derived on structs, `PartialOrd` compares two instances by comparing the value in each field in the order in which the fields appear in the struct @@ -111,9 +111,9 @@ a data structure that stores data based on the sort order of the values. The `Clone` trait allows you to explicitly create a deep copy of a value, and the duplication process might involve running arbitrary code and copying heap -data. See the [“Ways Variables and Data Interact: -Clone”][ways-variables-and-data-interact-clone] section in -Chapter 4 for more information on `Clone`. +data. See [Variables and Data Interacting with +Clone”][variables-and-data-interacting-with-clone] in Chapter 4 +for more information on `Clone`. Deriving `Clone` implements the `clone` method, which when implemented for the whole type, calls `clone` on each of the parts of the type. This means all the @@ -125,9 +125,9 @@ returned from `to_vec` will need to own its instances, so `to_vec` calls `clone` on each item. Thus, the type stored in the slice must implement `Clone`. The `Copy` trait allows you to duplicate a value by only copying bits stored on -the stack; no arbitrary code is necessary. See the [“Stack-Only Data: -Copy”][stack-only-data-copy] section in Chapter 4 for more -information on `Copy`. +the stack; no arbitrary code is necessary. See [“Stack-Only Data: +Copy”][stack-only-data-copy] in Chapter 4 for more information on +`Copy`. The `Copy` trait doesn’t define any methods to prevent programmers from overloading those methods and violating the assumption that no arbitrary code @@ -166,11 +166,11 @@ meaning all fields or values in the type must also implement `Default` to derive `Default`. The `Default::default` function is commonly used in combination with the struct -update syntax discussed in the [“Creating Instances From Other Instances With -Struct Update -Syntax”][creating-instances-from-other-instances-with-struct-update-syntax] -section in Chapter 5. You can customize a few fields of a struct and then -set and use a default value for the rest of the fields by using +update syntax discussed in [“Creating Instances From Other Instances With Struct +Update +Syntax”][creating-instances-from-other-instances-with-struct-update-syntax] in Chapter 5. You can customize a few fields of a struct and then set +and use a default value for the rest of the fields by using `..Default::default()`. The `Default` trait is required when you use the method `unwrap_or_default` on @@ -180,5 +180,5 @@ The `Default` trait is required when you use the method `unwrap_or_default` on [creating-instances-from-other-instances-with-struct-update-syntax]: ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax [stack-only-data-copy]: ch04-01-what-is-ownership.html#stack-only-data-copy -[ways-variables-and-data-interact-clone]: ch04-01-what-is-ownership.html#ways-variables-and-data-interact-clone +[variables-and-data-interacting-with-clone]: ch04-01-what-is-ownership.html#variables-and-data-interacting-with-clone [macros]: ch20-05-macros.html#macros diff --git a/src/ch00-00-introduction.md b/src/ch00-00-introduction.md index c2c5fa6ec2..4689d6acd3 100644 --- a/src/ch00-00-introduction.md +++ b/src/ch00-00-introduction.md @@ -74,8 +74,8 @@ mean both how quickly Rust code can run and the speed at which Rust lets you write programs. The Rust compiler’s checks ensure stability through feature additions and refactoring. This is in contrast to the brittle legacy code in languages without these checks, which developers are often afraid to modify. By -striving for zero-cost abstractions, higher-level features that compile to -lower-level code as fast as code written manually, Rust endeavors to make safe +striving for zero-cost abstractions—higher-level features that compile to +lower-level code as fast as code written manually—Rust endeavors to make safe code be fast code as well. The Rust language hopes to support many other users as well; those mentioned @@ -157,13 +157,13 @@ more about lifetimes, traits, types, functions, and closures. In Chapter 21, we’ll complete a project in which we’ll implement a low-level multithreaded web server! -Finally, some appendices contain useful information about the language in a -more reference-like format. Appendix A covers Rust’s keywords, Appendix B -covers Rust’s operators and symbols, Appendix C covers derivable traits -provided by the standard library, Appendix D covers some useful development -tools, and Appendix E explains Rust editions. In Appendix F, you can find -translations of the book, and in Appendix G we’ll cover how Rust is made and -what nightly Rust is. +Finally, some appendixes contain useful information about the language in a more +reference-like format. **Appendix A** covers Rust’s keywords, **Appendix B** +covers Rust’s operators and symbols, **Appendix C** covers derivable traits +provided by the standard library, **Appendix D** covers some useful development +tools, and **Appendix E** explains Rust editions. In **Appendix F**, you can +find translations of the book, and in **Appendix G** we’ll cover how Rust is +made and what nightly Rust is. There is no wrong way to read this book: if you want to skip ahead, go for it! You might have to jump back to earlier chapters if you experience any diff --git a/src/ch01-03-hello-cargo.md b/src/ch01-03-hello-cargo.md index 048ee864c5..6c6332b5a4 100644 --- a/src/ch01-03-hello-cargo.md +++ b/src/ch01-03-hello-cargo.md @@ -66,8 +66,6 @@ name = "hello_cargo" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] ``` diff --git a/src/ch02-00-guessing-game-tutorial.md b/src/ch02-00-guessing-game-tutorial.md index e73918ba62..9aed0208e0 100644 --- a/src/ch02-00-guessing-game-tutorial.md +++ b/src/ch02-00-guessing-game-tutorial.md @@ -857,7 +857,7 @@ If `parse` is _not_ able to turn the string into a number, it will return an `Err` value that contains more information about the error. The `Err` value does not match the `Ok(num)` pattern in the first `match` arm, but it does match the `Err(_)` pattern in the second arm. The underscore, `_`, is a -catchall value; in this example, we’re saying we want to match all `Err` +catch-all value; in this example, we’re saying we want to match all `Err` values, no matter what information they have inside them. So the program will execute the second arm’s code, `continue`, which tells the program to go to the next iteration of the `loop` and ask for another guess. So, effectively, the diff --git a/src/ch03-02-data-types.md b/src/ch03-02-data-types.md index e6d88fbb1d..bfebc9d384 100644 --- a/src/ch03-02-data-types.md +++ b/src/ch03-02-data-types.md @@ -62,11 +62,11 @@ when it’s safe to assume the number is positive, it’s shown with no sign. Signed numbers are stored using [two’s complement][twos-complement] representation. -Each signed variant can store numbers from -(2n - 1) to 2n - -1 - 1 inclusive, where _n_ is the number of bits that variant uses. So an -`i8` can store numbers from -(27) to 27 - 1, which equals --128 to 127. Unsigned variants can store numbers from 0 to 2n - 1, -so a `u8` can store numbers from 0 to 28 - 1, which equals 0 to 255. +Each signed variant can store numbers from −(2n − 1) to 2n − +1 − 1 inclusive, where _n_ is the number of bits that variant uses. So an +`i8` can store numbers from −(27) to 27 − 1, which equals +−128 to 127. Unsigned variants can store numbers from 0 to 2n − 1, +so a `u8` can store numbers from 0 to 28 − 1, which equals 0 to 255. Additionally, the `isize` and `usize` types depend on the architecture of the computer your program is running on, which is denoted in the table as “arch”: @@ -120,7 +120,7 @@ some sort of collection. > > - Wrap in all modes with the `wrapping_*` methods, such as `wrapping_add`. > - Return the `None` value if there is overflow with the `checked_*` methods. -> - Return the value and a boolean indicating whether there was overflow with +> - Return the value and a Boolean indicating whether there was overflow with > the `overflowing_*` methods. > - Saturate at the value’s minimum or maximum values with the `saturating_*` > methods. diff --git a/src/ch03-04-comments.md b/src/ch03-04-comments.md index 01978d605f..490afc6c50 100644 --- a/src/ch03-04-comments.md +++ b/src/ch03-04-comments.md @@ -16,9 +16,9 @@ comment continues until the end of the line. For comments that extend beyond a single line, you’ll need to include `//` on each line, like this: ```rust -// So we’re doing something complicated here, long enough that we need +// So we're doing something complicated here, long enough that we need // multiple lines of comments to do it! Whew! Hopefully, this comment will -// explain what’s going on. +// explain what's going on. ``` Comments can also be placed at the end of lines containing code: diff --git a/src/ch04-03-slices.md b/src/ch04-03-slices.md index 440653e430..ca7a8e4e2d 100644 --- a/src/ch04-03-slices.md +++ b/src/ch04-03-slices.md @@ -114,10 +114,10 @@ A _string slice_ is a reference to part of a `String`, and it looks like this: Rather than a reference to the entire `String`, `hello` is a reference to a portion of the `String`, specified in the extra `[0..5]` bit. We create slices using a range within brackets by specifying `[starting_index..ending_index]`, -where `starting_index` is the first position in the slice and `ending_index` is -one more than the last position in the slice. Internally, the slice data +where _`starting_index`_ is the first position in the slice and _`ending_index`_ +is one more than the last position in the slice. Internally, the slice data structure stores the starting position and the length of the slice, which -corresponds to `ending_index` minus `starting_index`. So, in the case of `let +corresponds to _`ending_index`_ minus _`starting_index`_. So, in the case of `let world = &s[6..11];`, `world` would be a slice that contains a pointer to the byte at index 6 of `s` with a length value of `5`. diff --git a/src/ch06-02-match.md b/src/ch06-02-match.md index 79800d786b..6fa9de9e76 100644 --- a/src/ch06-02-match.md +++ b/src/ch06-02-match.md @@ -193,7 +193,7 @@ possibility in order for the code to be valid. Especially in the case of `None` case, it protects us from assuming that we have a value when we might have null, thus making the billion-dollar mistake discussed earlier impossible. -### Catch-all Patterns and the `_` Placeholder +### Catch-All Patterns and the `_` Placeholder Using enums, we can also take special actions for a few particular values, but for all other values take one default action. Imagine we’re implementing a game diff --git a/src/ch07-00-managing-growing-projects-with-packages-crates-and-modules.md b/src/ch07-00-managing-growing-projects-with-packages-crates-and-modules.md index 1786711d8d..2bdf52822a 100644 --- a/src/ch07-00-managing-growing-projects-with-packages-crates-and-modules.md +++ b/src/ch07-00-managing-growing-projects-with-packages-crates-and-modules.md @@ -12,7 +12,7 @@ optionally one library crate. As a package grows, you can extract parts into separate crates that become external dependencies. This chapter covers all these techniques. For very large projects comprising a set of interrelated packages that evolve together, Cargo provides _workspaces_, which we’ll cover -in the [“Cargo Workspaces”][workspaces] section in Chapter 14. +in [“Cargo Workspaces”][workspaces] in Chapter 14. We’ll also discuss encapsulating implementation details, which lets you reuse code at a higher level: once you’ve implemented an operation, other code can diff --git a/src/ch07-01-packages-and-crates.md b/src/ch07-01-packages-and-crates.md index 281f27b7b6..1f91b69db6 100644 --- a/src/ch07-01-packages-and-crates.md +++ b/src/ch07-01-packages-and-crates.md @@ -4,14 +4,14 @@ The first parts of the module system we’ll cover are packages and crates. A _crate_ is the smallest amount of code that the Rust compiler considers at a time. Even if you run `rustc` rather than `cargo` and pass a single source code -file (as we did all the way back in the “Writing and Running a Rust Program” -section of Chapter 1), the compiler considers that file to be a crate. Crates -can contain modules, and the modules may be defined in other files that get -compiled with the crate, as we’ll see in the coming sections. +file (as we did all the way back in “Writing and Running a Rust Program” in +Chapter 1), the compiler considers that file to be a crate. Crates can contain +modules, and the modules may be defined in other files that get compiled with +the crate, as we’ll see in the coming sections. A crate can come in one of two forms: a binary crate or a library crate. _Binary crates_ are programs you can compile to an executable that you can run, -such as a command-line program or a server. Each must have a function called +such as a command line program or a server. Each must have a function called `main` that defines what happens when the executable runs. All the crates we’ve created so far have been binary crates. @@ -23,17 +23,16 @@ Most of the time when Rustaceans say “crate”, they mean library crate, and t 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 -[“Defining Modules to Control Scope and Privacy”][modules] -section). +up the root module of your crate (we’ll explain modules in depth in [“Defining +Modules to Control Scope and Privacy”][modules]). A _package_ is a bundle of one or more crates that provides a set of functionality. A package contains a _Cargo.toml_ file that describes how to 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 +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 package can contain as many binary crates as you +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. diff --git a/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md b/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md index a336f94a69..26ca52ef98 100644 --- a/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md +++ b/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md @@ -108,7 +108,7 @@ access to the `add_to_waitlist` function in the child module, so we mark the ```rust,ignore,does_not_compile -{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-05/src/lib.rs}} +{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-05/src/lib.rs:here}} ``` @@ -143,7 +143,7 @@ keyword before its definition, as in Listing 7-7. ```rust,noplayground,test_harness -{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-07/src/lib.rs}} +{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-07/src/lib.rs:here}} ``` @@ -174,7 +174,7 @@ If you plan on sharing your library crate so other projects can use your code, your public API is your contract with users of your crate that determines how they can interact with your code. There are many considerations around managing 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 +crate. These considerations are beyond the scope of this book; if you’re interested in this topic, see [The Rust API Guidelines][api-guidelines]. > #### Best Practices for Packages with a Binary and a Library @@ -195,7 +195,7 @@ interested in this topic, see [The Rust API Guidelines][api-guidelines]. > client! > > In [Chapter 12][ch12], we’ll demonstrate this organizational -> practice with a command-line program that will contain both a binary crate +> practice with a command line program that will contain both a binary crate > and a library crate. ### Starting Relative Paths with `super` @@ -268,7 +268,7 @@ have such a function, we couldn’t create an instance of `Breakfast` in In contrast, if we make an enum public, all of its variants are then public. We only need the `pub` before the `enum` keyword, as shown in Listing 7-10. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-10/src/lib.rs}} diff --git a/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md b/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md index 97b2b656a8..efff28e580 100644 --- a/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md +++ b/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md @@ -31,7 +31,7 @@ Note that `use` only creates the shortcut for the particular scope in which the child module named `customer`, which is then a different scope than the `use` statement, so the function body won’t compile. -+ ```rust,noplayground,test_harness,does_not_compile,ignore {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-12/src/lib.rs}} @@ -159,13 +159,12 @@ Re-exporting is useful when the internal structure of your code is different from how programmers calling your code would think about the domain. For example, in this restaurant metaphor, the people running the restaurant think about “front of house” and “back of house.” But customers visiting a restaurant -probably won’t think about the parts of the restaurant in those terms. With -`pub use`, we can write our code with one structure but expose a different -structure. Doing so makes our library well organized for programmers working on -the library and programmers calling the library. We’ll look at another example -of `pub use` and how it affects your crate’s documentation in the [“Exporting a -Convenient Public API with `pub use`”][ch14-pub-use] section of -Chapter 14. +probably won’t think about the parts of the restaurant in those terms. With `pub +use`, we can write our code with one structure but expose a different structure. +Doing so makes our library well organized for programmers working on the library +and programmers calling the library. We’ll look at another example of `pub use` +and how it affects your crate’s documentation in [“Exporting a Convenient Public +API with `pub use`”][ch14-pub-use] in Chapter 14. ### Using External Packages @@ -192,10 +191,10 @@ Adding `rand` as a dependency in _Cargo.toml_ tells Cargo to download the make `rand` available to our project. Then, to bring `rand` definitions into the scope of our package, we added a -`use` line starting with the name of the crate, `rand`, and listed the items -we wanted to bring into scope. Recall that in the [“Generating a Random -Number”][rand] section in Chapter 2, we brought the `Rng` trait -into scope and called the `rand::thread_rng` function: +`use` line starting with the name of the crate, `rand`, and listed the items we +wanted to bring into scope. Recall that in [“Generating a Random +Number”][rand] in Chapter 2, we brought the `Rng` trait into +scope and called the `rand::thread_rng` function: ```rust,ignore {{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-03/src/main.rs:ch07-04}} @@ -292,11 +291,12 @@ the current scope. Be careful when using the glob operator! Glob can make it harder to tell what names are in scope and where a name used in your program was defined. -The glob operator is often used when testing to bring everything under test -into the `tests` module; we’ll talk about that in the [“How to Write -Tests”][writing-tests] section in Chapter 11. The glob operator -is also sometimes used as part of the prelude pattern: see [the standard library documentation](../std/prelude/index.html#other-preludes) -for more information on that pattern. +The glob operator is often used when testing to bring everything under test into +the `tests` module; we’ll talk about that in [“How to Write +Tests”][writing-tests] in Chapter 11. The glob operator is also +sometimes used as part of the prelude pattern: see [the standard library +documentation](../std/prelude/index.html#other-preludes) for more +information on that pattern. [ch14-pub-use]: ch14-02-publishing-to-crates-io.html#exporting-a-convenient-public-api-with-pub-use [rand]: ch02-00-guessing-game-tutorial.html#generating-a-random-number diff --git a/src/ch08-00-common-collections.md b/src/ch08-00-common-collections.md index 7cc5c424b8..adf600c942 100644 --- a/src/ch08-00-common-collections.md +++ b/src/ch08-00-common-collections.md @@ -3,12 +3,12 @@ Rust’s standard library includes a number of very useful data structures called _collections_. Most other data types represent one specific value, but collections can contain multiple values. Unlike the built-in array and tuple -types, the data these collections point to is stored on the heap, which means -the amount of data does not need to be known at compile time and can grow or -shrink as the program runs. Each kind of collection has different capabilities -and costs, and choosing an appropriate one for your current situation is a -skill you’ll develop over time. In this chapter, we’ll discuss three -collections that are used very often in Rust programs: +types, the data that these collections point to is stored on the heap, which +means the amount of data does not need to be known at compile time and can grow +or shrink as the program runs. Each kind of collection has different +capabilities and costs, and choosing an appropriate one for your current +situation is a skill you’ll develop over time. In this chapter, we’ll discuss +three collections that are used very often in Rust programs: - A _vector_ allows you to store a variable number of values next to each other. - A _string_ is a collection of characters. We’ve mentioned the `String` type diff --git a/src/ch08-01-vectors.md b/src/ch08-01-vectors.md index d711c9904e..e787e970c9 100644 --- a/src/ch08-01-vectors.md +++ b/src/ch08-01-vectors.md @@ -185,8 +185,7 @@ will add `50` to each element. To change the value that the mutable reference refers to, we have to use the `*` dereference operator to get to the value in `i` before we can use the `+=` operator. We’ll talk more about the dereference operator in the [“Following the -Pointer to the Value with the Dereference Operator”][deref] -section of Chapter 15. +Pointer to the Value”][deref] section of Chapter 15. Iterating over a vector, whether immutably or mutably, is safe because of the borrow checker’s rules. If we attempted to insert or remove items in the `for` diff --git a/src/ch08-03-hash-maps.md b/src/ch08-03-hash-maps.md index 3db150419b..4d185c84c9 100644 --- a/src/ch08-03-hash-maps.md +++ b/src/ch08-03-hash-maps.md @@ -63,7 +63,7 @@ handles the `Option` by calling `copied` to get an `Option` rather than an `Option<&i32>`, then `unwrap_or` to set `score` to zero if `scores` doesn’t have an entry for the key. -We can iterate over each key–value pair in a hash map in a similar manner as we +We can iterate over each key-value pair in a hash map in a similar manner as we do with vectors, using a `for` loop: ```rust @@ -97,9 +97,8 @@ they’ve been moved into the hash map with the call to `insert`. If we insert references to values into the hash map, the values won’t be moved into the hash map. The values that the references point to must be valid for at least as long as the hash map is valid. We’ll talk more about these issues in -the [“Validating References with -Lifetimes”][validating-references-with-lifetimes] section in -Chapter 10. +[“Validating References with +Lifetimes”][validating-references-with-lifetimes] in Chapter 10. ### Updating a Hash Map @@ -120,7 +119,7 @@ new value. Let’s look at how to do each of these! If we insert a key and a value into a hash map and then insert that same key with a different value, the value associated with that key will be replaced. Even though the code in Listing 8-23 calls `insert` twice, the hash map will -only contain one key–value pair because we’re inserting the value for the Blue +only contain one key-value pair because we’re inserting the value for the Blue team’s key both times. @@ -190,9 +189,9 @@ the value `0`. This code will print `{"world": 2, "hello": 1, "wonderful": 1}`. You might see -the same key–value pairs printed in a different order: recall from the -[“Accessing Values in a Hash Map”][access] section that -iterating over a hash map happens in an arbitrary order. +the same key-value pairs printed in a different order: recall from [“Accessing +Values in a Hash Map”][access] that iterating over a hash map +happens in an arbitrary order. The `split_whitespace` method returns an iterator over subslices, separated by whitespace, of the value in `text`. The `or_insert` method returns a mutable diff --git a/src/ch09-00-error-handling.md b/src/ch09-00-error-handling.md index a5240b1997..ad49b70a86 100644 --- a/src/ch09-00-error-handling.md +++ b/src/ch09-00-error-handling.md @@ -4,8 +4,8 @@ Errors are a fact of life in software, so Rust has a number of features for handling situations in which something goes wrong. In many cases, Rust requires you to acknowledge the possibility of an error and take some action before your code will compile. This requirement makes your program more robust by ensuring -that you’ll discover errors and handle them appropriately before you’ve -deployed your code to production! +that you’ll discover errors and handle them appropriately before deploying your +code to production! Rust groups errors into two major categories: _recoverable_ and _unrecoverable_ errors. For a recoverable error, such as a _file not found_ error, we most diff --git a/src/ch09-02-recoverable-errors-with-result.md b/src/ch09-02-recoverable-errors-with-result.md index 13117d7dfb..85c02d4061 100644 --- a/src/ch09-02-recoverable-errors-with-result.md +++ b/src/ch09-02-recoverable-errors-with-result.md @@ -502,15 +502,15 @@ code will now compile. -The `Box` type is a _trait object_, which we’ll talk about in the -[“Using Trait Objects that Allow for Values of Different -Types”][trait-objects] section in Chapter 18. For now, you can -read `Box` to mean “any kind of error.” Using `?` on a `Result` -value in a `main` function with the error type `Box` is allowed -because it allows any `Err` value to be returned early. Even though the body of -this `main` function will only ever return errors of type `std::io::Error`, by -specifying `Box`, this signature will continue to be correct even if -more code that returns other errors is added to the body of `main`. +The `Box` type is a _trait object_, which we’ll talk about in [“Using +Trait Objects That Allow for Values of Different Types”][trait-objects] in Chapter 18. For now, you can read `Box` to mean “any +kind of error.” Using `?` on a `Result` value in a `main` function with the +error type `Box` is allowed because it allows any `Err` value to be +returned early. Even though the body of this `main` function will only ever +return errors of type `std::io::Error`, by specifying `Box`, this +signature will continue to be correct even if more code that returns other +errors is added to the body of `main`. When a `main` function returns a `Result<(), E>`, the executable will exit with a value of `0` if `main` returns `Ok(())` and will exit with a nonzero value if diff --git a/src/ch09-03-to-panic-or-not-to-panic.md b/src/ch09-03-to-panic-or-not-to-panic.md index 90d9eacfd4..66cba1fd81 100644 --- a/src/ch09-03-to-panic-or-not-to-panic.md +++ b/src/ch09-03-to-panic-or-not-to-panic.md @@ -77,8 +77,8 @@ more of the following: - Your code after this point needs to rely on not being in this bad state, rather than checking for the problem at every step. - There’s not a good way to encode this information in the types you use. We’ll - work through an example of what we mean in the [“Encoding States and Behavior - as Types”][encoding] section of Chapter 18. + work through an example of what we mean in [“Encoding States and Behavior as + Types”][encoding] in Chapter 18. If someone calls your code and passes in values that don’t make sense, it’s best to return an error if you can so the user of the library can decide what diff --git a/src/ch10-01-syntax.md b/src/ch10-01-syntax.md index fa8b5b1eb0..663844423e 100644 --- a/src/ch10-01-syntax.md +++ b/src/ch10-01-syntax.md @@ -33,8 +33,8 @@ To parameterize the types in a new single function, we need to name the type parameter, just as we do for the value parameters to a function. You can use any identifier as a type parameter name. But we’ll use `T` because, by convention, type parameter names in Rust are short, often just one letter, and -Rust’s type-naming convention is UpperCamelCase. Short for _type_, `T` is the -default choice of most Rust programmers. +Rust’s type-naming convention is CamelCase. Short for _type_, `T` is the default +choice of most Rust programmers. When we use a parameter in the body of the function, we have to declare the parameter name in the signature so the compiler knows what that name means. diff --git a/src/ch10-03-lifetime-syntax.md b/src/ch10-03-lifetime-syntax.md index 6b92470cd6..e47a49d570 100644 --- a/src/ch10-03-lifetime-syntax.md +++ b/src/ch10-03-lifetime-syntax.md @@ -14,9 +14,9 @@ when the lifetimes of references could be related in a few different ways. Rust requires us to annotate the relationships using generic lifetime parameters to ensure the actual references used at runtime will definitely be valid. -Annotating lifetimes is not a concept most other programming languages have, so -this is going to feel unfamiliar. Although we won’t cover lifetimes in their -entirety in this chapter, we’ll discuss common ways you might encounter +Annotating lifetimes is not even a concept most other programming languages +have, so this is going to feel unfamiliar. Although we won’t cover lifetimes in +their entirety in this chapter, we’ll discuss common ways you might encounter lifetime syntax so you can get comfortable with the concept. ### Preventing Dangling References with Lifetimes @@ -34,7 +34,7 @@ scope. -> Note: The examples in Listing 10-16, 10-17, and 10-23 declare variables +> Note: The examples in Listings 10-16, 10-17, and 10-23 declare variables > without giving them an initial value, so the variable name exists in the outer > scope. At first glance, this might appear to be in conflict with Rust’s having > no null values. However, if we try to use a variable before giving it a value, @@ -96,7 +96,7 @@ Here, `x` has the lifetime `'b`, which in this case is larger than `'a`. This means `r` can reference `x` because Rust knows that the reference in `r` will always be valid while `x` is valid. -Now that you know what the lifetimes of references are and how Rust analyzes +Now that you know where the lifetimes of references are and how Rust analyzes lifetimes to ensure references will always be valid, let’s explore generic lifetimes of parameters and return values in the context of functions. @@ -117,10 +117,10 @@ print `The longest string is abcd`. Note that we want the function to take string slices, which are references, rather than strings, because we don’t want the `longest` function to take -ownership of its parameters. Refer to the [“String Slices as -Parameters”][string-slices-as-parameters] section in Chapter 4 -for more discussion about why the parameters we use in Listing 10-19 are the -ones we want. +ownership of its parameters. Refer to [“String Slices as +Parameters”][string-slices-as-parameters] in Chapter 4 for more +discussion about why the parameters we use in Listing 10-19 are the ones we +want. If we try to implement the `longest` function as shown in Listing 10-20, it won’t compile. @@ -518,7 +518,7 @@ annotate lifetimes in method signatures very often. ### Lifetime Annotations in Method Definitions When we implement methods on a struct with lifetimes, we use the same syntax as -that of generic type parameters shown in Listing 10-11. Where we declare and +that of generic type parameters, as shown in Listing 10-11. Where we declare and use the lifetime parameters depends on whether they’re related to the struct fields or the method parameters and return values. diff --git a/src/ch11-00-testing.md b/src/ch11-00-testing.md index f6e799d581..b227072ef3 100644 --- a/src/ch11-00-testing.md +++ b/src/ch11-00-testing.md @@ -1,9 +1,9 @@ # Writing Automated Tests -In his 1972 essay “The Humble Programmer,” Edsger W. Dijkstra said that -“Program testing can be a very effective way to show the presence of bugs, but -it is hopelessly inadequate for showing their absence.” That doesn’t mean we -shouldn’t try to test as much as we can! +In his 1972 essay “The Humble Programmer,” Edsger W. Dijkstra said that “program +testing can be a very effective way to show the presence of bugs, but it is +hopelessly inadequate for showing their absence.” That doesn’t mean we shouldn’t +try to test as much as we can! Correctness in our programs is the extent to which our code does what we intend it to do. Rust is designed with a high degree of concern about the correctness diff --git a/src/ch11-01-writing-tests.md b/src/ch11-01-writing-tests.md index b04f0c2fee..63afae0930 100644 --- a/src/ch11-01-writing-tests.md +++ b/src/ch11-01-writing-tests.md @@ -96,18 +96,16 @@ passed; 0 failed` totals the number of tests that passed or failed. It’s possible to mark a test as ignored so it doesn’t run in a particular instance; we’ll cover that in the [“Ignoring Some Tests Unless Specifically Requested”][ignoring] section later in this chapter. Because we -haven’t done that here, the summary shows `0 ignored`. +haven’t done that here, the summary shows `0 ignored`. We can also pass an +argument to the `cargo test` command to run only tests whose name matches a +string; this is called _filtering_ and we’ll cover that in the [“Running a +Subset of Tests by Name”][subset] section. Here we haven’t +filtered the tests being run, so the end of the summary shows `0 filtered out`. The `0 measured` statistic is for benchmark tests that measure performance. Benchmark tests are, as of this writing, only available in nightly Rust. See [the documentation about benchmark tests][bench] to learn more. -We can pass an argument to the `cargo test` command to run only tests whose -name matches a string; this is called _filtering_ and we’ll cover that in the -[“Running a Subset of Tests by Name”][subset] section. Here we -haven’t filtered the tests being run, so the end of the summary shows `0 -filtered out`. - The next part of the test output starting at `Doc-tests adder` is for the results of any documentation tests. We don’t have any documentation tests yet, but Rust can compile any code examples that appear in our API documentation. @@ -317,19 +315,19 @@ Run the tests again: {{#include ../listings/ch11-writing-automated-tests/no-listing-04-bug-in-add-two/output.txt}} ``` -Our test caught the bug! The `it_adds_two` test failed, and the message tells -us ``assertion `left == right` failed`` and what the `left` and `right` values -are. This message helps us start debugging: the `left` argument, where we had -the result of calling `add_two(2)`, was `5` but the `right` argument was `4`. -You can imagine that this would be especially helpful when we have a lot of -tests going on. +Our test caught the bug! The `it_adds_two` test failed, and the message tells us +that the assertion that failed was ``assertion `left == right` failed`` and what +the `left` and `right` values are. This message helps us start debugging: the +`left` argument, where we had the result of calling `add_two(2)`, was `5` but +the `right` argument was `4`. You can imagine that this would be especially +helpful when we have a lot of tests going on. Note that in some languages and test frameworks, the parameters to equality assertion functions are called `expected` and `actual`, and the order in which we specify the arguments matters. However, in Rust, they’re called `left` and -`right`, and the order in which we specify the value we expect and the value -the code produces doesn’t matter. We could write the assertion in this test as -`assert_eq!(4, result)`, which would produce the same failure message +`right`, and the order in which we specify the value we expect and the value the +code produces doesn’t matter. We could write the assertion in this test as +`assert_eq!(4, result)`, which would result in the same failure message that that displays `` assertion failed: `(left == right)` ``. The `assert_ne!` macro will pass if the two values we give it are not equal and @@ -358,13 +356,12 @@ details about these and other derivable traits. You can also add a custom message to be printed with the failure message as optional arguments to the `assert!`, `assert_eq!`, and `assert_ne!` macros. Any arguments specified after the required arguments are passed along to the -`format!` macro (discussed in Chapter 8 in the [“Concatenation with the `+` -Operator or the `format!` -Macro”][concatenation-with-the--operator-or-the-format-macro] -section), so you can pass a format string that contains `{}` placeholders and -values to go in those placeholders. Custom messages are useful for documenting -what an assertion means; when a test fails, you’ll have a better idea of what -the problem is with the code. +`format!` macro (discussed in [“Concatenation with the `+` Operator or the +`format!` Macro”][concatenation-with-the--operator-or-the-format-macro] in Chapter 8), so you can pass a format string that contains `{}` +placeholders and values to go in those placeholders. Custom messages are useful +for documenting what an assertion means; when a test fails, you’ll have a better +idea of what the problem is with the code. For example, let’s say we have a function that greets people by name and we want to test that the name we pass into the function appears in the output: diff --git a/src/ch11-03-test-organization.md b/src/ch11-03-test-organization.md index d0c6ef3003..411c41b7b4 100644 --- a/src/ch11-03-test-organization.md +++ b/src/ch11-03-test-organization.md @@ -67,12 +67,12 @@ Consider the code in Listing 11-12 with the private function `internal_adder`. Note that the `internal_adder` function is not marked as `pub`. Tests are just Rust code, and the `tests` module is just another module. As we discussed in -the [“Paths for Referring to an Item in the Module Tree”][paths] -section, items in child modules can use the items in their ancestor modules. In -this test, we bring all of the `tests` module’s parent’s items into scope with -`use super::*`, and then the test can call `internal_adder`. If you don’t think -private functions should be tested, there’s nothing in Rust that will compel -you to do so. +[“Paths for Referring to an Item in the Module Tree”][paths], +items in child modules can use the items in their ancestor modules. In this +test, we bring all of the `tests` module’s parent’s items into scope with `use +super::*`, and then the test can call `internal_adder`. If you don’t think +private functions should be tested, there’s nothing in Rust that will compel you +to do so. ### Integration Tests @@ -206,14 +206,13 @@ project directory now looks like this: └── integration_test.rs ``` -This is the older naming convention that Rust also understands that we -mentioned in the [“Alternate File Paths”][alt-paths] section of -Chapter 7. Naming the file this way tells Rust not to treat the `common` module -as an integration test file. When we move the `setup` function code into -_tests/common/mod.rs_ and delete the _tests/common.rs_ file, the section in the -test output will no longer appear. Files in subdirectories of the _tests_ -directory don’t get compiled as separate crates or have sections in the test -output. +This is the older naming convention that Rust also understands that we mentioned +in [“Alternate File Paths”][alt-paths] in Chapter 7. Naming the +file this way tells Rust not to treat the `common` module as an integration test +file. When we move the `setup` function code into _tests/common/mod.rs_ and +delete the _tests/common.rs_ file, the section in the test output will no longer +appear. Files in subdirectories of the _tests_ directory don’t get compiled as +separate crates or have sections in the test output. After we’ve created _tests/common/mod.rs_, we can use it from any of the integration test files as a module. Here’s an example of calling the `setup` diff --git a/src/ch15-00-smart-pointers.md b/src/ch15-00-smart-pointers.md index 323463bf40..0565abd047 100644 --- a/src/ch15-00-smart-pointers.md +++ b/src/ch15-00-smart-pointers.md @@ -33,8 +33,8 @@ struct, smart pointers implement the `Deref` and `Drop` traits. The `Deref` trait allows an instance of the smart pointer struct to behave like a reference so you can write your code to work with either references or smart pointers. The `Drop` trait allows you to customize the code that’s run when an instance -of the smart pointer goes out of scope. In this chapter, we’ll discuss both -traits and demonstrate why they’re important to smart pointers. +of the smart pointer goes out of scope. In this chapter, we’ll discuss both of +these traits and demonstrate why they’re important to smart pointers. Given that the smart pointer pattern is a general design pattern used frequently in Rust, this chapter won’t cover every existing smart pointer. Many diff --git a/src/ch15-01-box.md b/src/ch15-01-box.md index f53ee6aaa7..4244519352 100644 --- a/src/ch15-01-box.md +++ b/src/ch15-01-box.md @@ -45,7 +45,7 @@ Listing 15-1 shows how to use a box to store an `i32` value on the heap: We define the variable `b` to have the value of a `Box` that points to the value `5`, which is allocated on the heap. This program will print `b = 5`; in -this case, we can access the data in the box similar to how we would if this +this case, we can access the data in the box similarly to how we would if this data were on the stack. Just like any owned value, when a box goes out of scope, as `b` does at the end of `main`, it will be deallocated. The deallocation happens both for the box (stored on the stack) and the data it @@ -60,7 +60,7 @@ wouldn’t be allowed to if we didn’t have boxes. ### Enabling Recursive Types with Boxes A value of _recursive type_ can have another value of the same type as part of -itself. Recursive types pose an issue because at compile time Rust needs to +itself. Recursive types pose an issue because, at compile time, Rust needs to know how much space a type takes up. However, the nesting of values of recursive types could theoretically continue infinitely, so Rust can’t know how much space the value needs. Because boxes have a known size, we can enable @@ -92,8 +92,8 @@ Each item in a cons list contains two elements: the value of the current item and the next item. The last item in the list contains only a value called `Nil` without a next item. A cons list is produced by recursively calling the `cons` function. The canonical name to denote the base case of the recursion is `Nil`. -Note that this is not the same as the “null” or “nil” concept in Chapter 6, -which is an invalid or absent value. +Note that this is not the same as the “null” or “nil” concept discussed in +Chapter 6, which is an invalid or absent value. The cons list isn’t a commonly used data structure in Rust. Most of the time when you have a list of items in Rust, `Vec` is a better choice to use. diff --git a/src/ch15-02-deref.md b/src/ch15-02-deref.md index a0b00e1a7e..da79b04eaf 100644 --- a/src/ch15-02-deref.md +++ b/src/ch15-02-deref.md @@ -126,14 +126,18 @@ Our `MyBox` type can’t be dereferenced because we haven’t implemented tha ability on our type. To enable dereferencing with the `*` operator, we implement the `Deref` trait. -### Treating a Type Like a Reference by Implementing the `Deref` Trait + + + + +### Implementing the `Deref` Trait -As discussed in the [“Implementing a Trait on a Type”][impl-trait] section of Chapter 10, to implement a trait, we need to provide -implementations for the trait’s required methods. The `Deref` trait, provided -by the standard library, requires us to implement one method named `deref` that -borrows `self` and returns a reference to the inner data. Listing 15-10 -contains an implementation of `Deref` to add to the definition of `MyBox`: +As discussed in [“Implementing a Trait on a Type”][impl-trait] in +Chapter 10, to implement a trait, we need to provide implementations for the +trait’s required methods. The `Deref` trait, provided by the standard library, +requires us to implement one method named `deref` that borrows `self` and +returns a reference to the inner data. Listing 15-10 contains an implementation +of `Deref` to add to the definition of `MyBox`: @@ -157,7 +161,7 @@ calls `*` on the `MyBox` value now compiles, and the assertions pass! Without the `Deref` trait, the compiler can only dereference `&` references. The `deref` method gives the compiler the ability to take a value of any type -that implements `Deref` and call the `deref` method to get a `&` reference that +that implements `Deref` and call the `deref` method to get an `&` reference that it knows how to dereference. When we entered `*y` in Listing 15-9, behind the scenes Rust actually ran this @@ -269,9 +273,9 @@ operator on mutable references. Rust does deref coercion when it finds types and trait implementations in three cases: -- From `&T` to `&U` when `T: Deref` -- From `&mut T` to `&mut U` when `T: DerefMut` -- From `&mut T` to `&U` when `T: Deref` +1. From `&T` to `&U` when `T: Deref` +2. From `&mut T` to `&mut U` when `T: DerefMut` +3. From `&mut T` to `&U` when `T: Deref` The first two cases are the same as each other except that the second implements mutability. The first case states that if you have a `&T`, and `T` diff --git a/src/ch15-03-drop.md b/src/ch15-03-drop.md index 4f0b96bd97..0608e2af07 100644 --- a/src/ch15-03-drop.md +++ b/src/ch15-03-drop.md @@ -7,7 +7,7 @@ be used to release resources like files or network connections. We’re introducing `Drop` in the context of smart pointers because the functionality of the `Drop` trait is almost always used when implementing a -smart pointer. For example, when a `Box` is dropped it will deallocate the +smart pointer. For example, when a `Box` is dropped, it will deallocate the space on the heap that the box points to. In some languages, for some types, the programmer must call code to free memory @@ -94,7 +94,7 @@ When we try to compile this code, we’ll get this error: This error message states that we’re not allowed to explicitly call `drop`. The error message uses the term _destructor_, which is the general programming term -for a function that cleans up an instance. A _destructor_ is analogous to a +for a function that cleans up an instance. A destructor is analogous to a _constructor_, which creates an instance. The `drop` function in Rust is one particular destructor. diff --git a/src/ch15-04-rc.md b/src/ch15-04-rc.md index 6d1b243fc3..b1033aa3e3 100644 --- a/src/ch15-04-rc.md +++ b/src/ch15-04-rc.md @@ -127,9 +127,9 @@ then we can see how the reference count changes when `c` goes out of scope. At each point in the program where the reference count changes, we print the reference count, which we get by calling the `Rc::strong_count` function. This function is named `strong_count` rather than `count` because the `Rc` type -also has a `weak_count`; we’ll see what `weak_count` is used for in the -[“Preventing Reference Cycles: Turning an `Rc` into a -`Weak`”][preventing-ref-cycles] section. +also has a `weak_count`; we’ll see what `weak_count` is used for in [“Preventing +Reference Cycles: Turning an `Rc` into a +`Weak`”][preventing-ref-cycles]. This code prints the following: diff --git a/src/ch15-05-interior-mutability.md b/src/ch15-05-interior-mutability.md index 77180db94d..70c99577e0 100644 --- a/src/ch15-05-interior-mutability.md +++ b/src/ch15-05-interior-mutability.md @@ -310,16 +310,16 @@ variable named `value` so we can access it directly later. Then we create a than transferring ownership from `value` to `a` or having `a` borrow from `value`. -We wrap the list `a` in an `Rc` so when we create lists `b` and `c`, they -can both refer to `a`, which is what we did in Listing 15-18. +We wrap the list `a` in an `Rc` so that when we create lists `b` and `c`, +they can both refer to `a`, which is what we did in Listing 15-18. After we’ve created the lists in `a`, `b`, and `c`, we want to add 10 to the value in `value`. We do this by calling `borrow_mut` on `value`, which uses the -automatic dereferencing feature we discussed in Chapter 5 (see the section -[“Where’s the `->` Operator?”][wheres-the---operator]) to -dereference the `Rc` to the inner `RefCell` value. The `borrow_mut` -method returns a `RefMut` smart pointer, and we use the dereference operator -on it and change the inner value. +automatic dereferencing feature we discussed in Chapter 5 (see [“Where’s the +`->` Operator?”][wheres-the---operator]) to dereference the +`Rc` to the inner `RefCell` value. The `borrow_mut` method returns a +`RefMut` smart pointer, and we use the dereference operator on it and change +the inner value. When we print `a`, `b`, and `c`, we can see that they all have the modified value of 15 rather than 5: diff --git a/src/ch15-06-reference-cycles.md b/src/ch15-06-reference-cycles.md index 91b176f3b6..afe056ffa0 100644 --- a/src/ch15-06-reference-cycles.md +++ b/src/ch15-06-reference-cycles.md @@ -70,7 +70,7 @@ this point, because its reference count is 1, not 0. Then Rust drops `a`, which decreases the reference count of the `a` `Rc` instance from 2 to 1 as well. This instance’s memory can’t be dropped either, because the other `Rc` instance still refers to it. The memory allocated to the list will -remain uncollected forever. To visualize this reference cycle, we’ve created a +remain uncollected forever. To visualize this reference cycle, we’ve created the diagram in Figure 15-4. Reference cycle of lists @@ -79,8 +79,8 @@ diagram in Figure 15-4. pointing to each other If you uncomment the last `println!` and run the program, Rust will try to -print this cycle with `a` pointing to `b` pointing to `a` and so forth until it -overflows the stack. +print this cycle with `a` pointing to `b` pointing to `a`, and so forth, until +it overflows the stack. Compared to a real-world program, the consequences of creating a reference cycle in this example aren’t very dire: right after we create the reference cycle, @@ -113,8 +113,8 @@ So far, we’ve demonstrated that calling `Rc::clone` increases the `strong_count` of an `Rc` instance, and an `Rc` instance is only cleaned up if its `strong_count` is 0. You can also create a _weak reference_ to the value within an `Rc` instance by calling `Rc::downgrade` and passing a -reference to the `Rc`. Strong references are how you can share ownership of -an `Rc` instance. Weak references don’t express an ownership relationship, +reference to the `Rc`. _Strong references_ are how you can share ownership of +an `Rc` instance. _Weak references_ don’t express an ownership relationship, and their count doesn’t affect when an `Rc` instance is cleaned up. They won’t cause a reference cycle because any cycle involving some weak references will be broken once the strong reference count of values involved is 0. @@ -266,7 +266,7 @@ in Listing 15-29: After `leaf` is created, its `Rc` has a strong count of 1 and a weak count of 0. In the inner scope, we create `branch` and associate it with -`leaf`, at which point when we print the counts, the `Rc` in `branch` +`leaf`, at which point, when we print the counts, the `Rc` in `branch` will have a strong count of 1 and a weak count of 1 (for `leaf.parent` pointing to `branch` with a `Weak`). When we print the counts in `leaf`, we’ll see it will have a strong count of 2, because `branch` now has a clone of the diff --git a/src/ch16-00-concurrency.md b/src/ch16-00-concurrency.md index 27293fd239..21511cd58f 100644 --- a/src/ch16-00-concurrency.md +++ b/src/ch16-00-concurrency.md @@ -1,11 +1,11 @@ # Fearless Concurrency Handling concurrent programming safely and efficiently is another of Rust’s -major goals. _Concurrent programming_, where different parts of a program -execute independently, and _parallel programming_, where different parts of a -program execute at the same time, are becoming increasingly important as more +major goals. _Concurrent programming_, in which different parts of a program +execute independently, and _parallel programming_, in which different parts of +a program execute at the same time, are becoming increasingly important as more computers take advantage of their multiple processors. Historically, -programming in these contexts has been difficult and error prone: Rust hopes to +programming in these contexts has been difficult and error prone. Rust hopes to change that. Initially, the Rust team thought that ensuring memory safety and preventing diff --git a/src/ch16-01-threads.md b/src/ch16-01-threads.md index 17d56ef7b5..bc72bbb546 100644 --- a/src/ch16-01-threads.md +++ b/src/ch16-01-threads.md @@ -4,7 +4,7 @@ In most current operating systems, an executed program’s code is run in a _process_, and the operating system will manage multiple processes at once. Within a program, you can also have independent parts that run simultaneously. The features that run these independent parts are called _threads_. For -example, a web server could have multiple threads so that it could respond to +example, a web server could have multiple threads so that it can respond to more than one request at the same time. Splitting the computation in your program into multiple threads to run multiple @@ -13,9 +13,9 @@ Because threads can run simultaneously, there’s no inherent guarantee about th order in which parts of your code on different threads will run. This can lead to problems, such as: -- Race conditions, where threads are accessing data or resources in an +- Race conditions, in which threads are accessing data or resources in an inconsistent order -- Deadlocks, where two threads are waiting for each other, preventing both +- Deadlocks, in which two threads are waiting for each other, preventing both threads from continuing - Bugs that happen only in certain situations and are hard to reproduce and fix reliably @@ -74,7 +74,7 @@ duration, allowing a different thread to run. The threads will probably take turns, but that isn’t guaranteed: it depends on how your operating system schedules the threads. In this run, the main thread printed first, even though the print statement from the spawned thread appears first in the code. And even -though we told the spawned thread to print until `i` is 9, it only got to 5 +though we told the spawned thread to print until `i` is `9`, it only got to `5` before the main thread shut down. If you run this code and only see output from the main thread, or don’t see any @@ -93,7 +93,7 @@ by saving the return value of `thread::spawn` in a variable. The return type of `thread::spawn` is `JoinHandle`. A `JoinHandle` is an owned value that, when we call the `join` method on it, will wait for its thread to finish. Listing 16-2 shows how to use the `JoinHandle` of the thread we created in Listing 16-1 and -call `join` to make sure the spawned thread finishes before `main` exits: +how to call `join` to make sure the spawned thread finishes before `main` exits: @@ -174,9 +174,9 @@ threads run at the same time. We'll often use the `move` keyword with closures passed to `thread::spawn` because the closure will then take ownership of the values it uses from the environment, thus transferring ownership of those values from one thread to -another. In the [“Capturing References or Moving Ownership”][capture] section of Chapter 13, we discussed `move` in the context of closures. Now, -we’ll concentrate more on the interaction between `move` and `thread::spawn`. +another. In [“Capturing References or Moving Ownership”][capture] +in Chapter 13, we discussed `move` in the context of closures. Now, we’ll +concentrate more on the interaction between `move` and `thread::spawn`. Notice in Listing 16-1 that the closure we pass to `thread::spawn` takes no arguments: we’re not using any data from the main thread in the spawned @@ -267,8 +267,8 @@ Rust’s ownership rules have saved us again! We got an error from the code in Listing 16-3 because Rust was being conservative and only borrowing `v` for the thread, which meant the main thread could theoretically invalidate the spawned thread’s reference. By telling Rust to move ownership of `v` to the spawned -thread, we’re guaranteeing Rust that the main thread won’t use `v` anymore. If -we change Listing 16-4 in the same way, we’re then violating the ownership +thread, we’re guaranteeing to Rust that the main thread won’t use `v` anymore. +If we change Listing 16-4 in the same way, we’re then violating the ownership rules when we try to use `v` in the main thread. The `move` keyword overrides Rust’s conservative default of borrowing; it doesn’t let us violate the ownership rules. diff --git a/src/ch16-02-message-passing.md b/src/ch16-02-message-passing.md index d5b3c91ec1..917a93dc15 100644 --- a/src/ch16-02-message-passing.md +++ b/src/ch16-02-message-passing.md @@ -6,7 +6,7 @@ containing data. Here’s the idea in a slogan from [the Go language documentati “Do not communicate by sharing memory; instead, share memory by communicating.” To accomplish message-sending concurrency, Rust's standard library provides an -implementation of _channels_. A channel is a general programming concept by +implementation of channels. A _channel_ is a general programming concept by which data is sent from one thread to another. You can imagine a channel in programming as being like a directional channel of @@ -61,9 +61,9 @@ this way is a convenient approach to extract the pieces of the tuple returned by `mpsc::channel`. Let’s move the transmitting end into a spawned thread and have it send one -string so the spawned thread is communicating with the main thread, as shown in -Listing 16-7. This is like putting a rubber duck in the river upstream or -sending a chat message from one thread to another. +string so that the spawned thread is communicating with the main thread, as +shown in Listing 16-7. This is like putting a rubber duck in the river upstream +or sending a chat message from one thread to another. @@ -74,13 +74,15 @@ sending a chat message from one thread to another. Again, we’re using `thread::spawn` to create a new thread and then using `move` -to move `tx` into the closure so the spawned thread owns `tx`. The spawned +to move `tx` into the closure so that the spawned thread owns `tx`. The spawned thread needs to own the transmitter to be able to send messages through the -channel. The transmitter has a `send` method that takes the value we want to -send. The `send` method returns a `Result` type, so if the receiver has -already been dropped and there’s nowhere to send a value, the send operation -will return an error. In this example, we’re calling `unwrap` to panic in case -of an error. But in a real application, we would handle it properly: return to +channel. + +The transmitter has a `send` method that takes the value we want to send. The +`send` method returns a `Result` type, so if the receiver has already +been dropped and there’s nowhere to send a value, the send operation will +return an error. In this example, we’re calling `unwrap` to panic in case of an +error. But in a real application, we would handle it properly: return to Chapter 9 to review strategies for proper error handling. In Listing 16-8, we’ll get the value from the receiver in the main thread. This diff --git a/src/ch16-03-shared-state.md b/src/ch16-03-shared-state.md index 52de2fb7dc..9f4936ce39 100644 --- a/src/ch16-03-shared-state.md +++ b/src/ch16-03-shared-state.md @@ -30,9 +30,9 @@ mutex is described as _guarding_ the data it holds via the locking system. Mutexes have a reputation for being difficult to use because you have to remember two rules: -- You must attempt to acquire the lock before using the data. -- When you’re done with the data that the mutex guards, you must unlock the - data so other threads can acquire the lock. +1. You must attempt to acquire the lock before using the data. +2. When you’re done with the data that the mutex guards, you must unlock the + data so other threads can acquire the lock. For a real-world metaphor for a mutex, imagine a panel discussion at a conference with only one microphone. Before a panelist can speak, they have to @@ -144,8 +144,8 @@ the `Rc` before moving ownership to the thread. -Once again, we compile and get... different errors! The compiler is teaching us -a lot. +Once again, we compile and get… different errors! The compiler is teaching us a +lot. ```console {{#include ../listings/ch16-fearless-concurrency/listing-16-14/output.txt}} @@ -164,14 +164,14 @@ subtracts from the count when each clone is dropped. But it doesn’t use any concurrency primitives to make sure that changes to the count can’t be interrupted by another thread. This could lead to wrong counts—subtle bugs that could in turn lead to memory leaks or a value being dropped before we’re done -with it. What we need is a type exactly like `Rc` but one that makes changes -to the reference count in a thread-safe way. +with it. What we need is a type that is exactly like `Rc` but one that makes +changes to the reference count in a thread-safe way. #### Atomic Reference Counting with `Arc` Fortunately, `Arc` _is_ a type like `Rc` that is safe to use in concurrent situations. The _a_ stands for _atomic_, meaning it’s an _atomically -reference counted_ type. Atomics are an additional kind of concurrency +reference-counted_ type. Atomics are an additional kind of concurrency primitive that we won’t cover in detail here: see the standard library documentation for [`std::sync::atomic`][atomic] for more details. At this point, you just need to know that atomics work like primitive @@ -221,14 +221,14 @@ type for this example so we could concentrate on how `Mutex` works. ### Similarities Between `RefCell`/`Rc` and `Mutex`/`Arc` -You might have noticed that `counter` is immutable but we could get a mutable +You might have noticed that `counter` is immutable, but we could get a mutable reference to the value inside it; this means `Mutex` provides interior mutability, as the `Cell` family does. In the same way we used `RefCell` in Chapter 15 to allow us to mutate contents inside an `Rc`, we use `Mutex` to mutate contents inside an `Arc`. Another detail to note is that Rust can’t protect you from all kinds of logic -errors when you use `Mutex`. Recall in Chapter 15 that using `Rc` came +errors when you use `Mutex`. Recall from Chapter 15 that using `Rc` came with the risk of creating reference cycles, where two `Rc` values refer to each other, causing memory leaks. Similarly, `Mutex` comes with the risk of creating _deadlocks_. These occur when an operation needs to lock two resources diff --git a/src/ch16-04-extensible-concurrency-sync-and-send.md b/src/ch16-04-extensible-concurrency-sync-and-send.md index 4bae9d68c6..22a909bed8 100644 --- a/src/ch16-04-extensible-concurrency-sync-and-send.md +++ b/src/ch16-04-extensible-concurrency-sync-and-send.md @@ -43,8 +43,8 @@ The smart pointer `Rc` is also not `Sync` for the same reasons that it’s no family of related `Cell` types are not `Sync`. The implementation of borrow checking that `RefCell` does at runtime is not thread-safe. The smart pointer `Mutex` is `Sync` and can be used to share access with multiple -threads as you saw in the [“Sharing a `Mutex` Between Multiple -Threads”][sharing-a-mutext-between-multiple-threads] section. +threads as you saw in [“Sharing a `Mutex` Between Multiple +Threads”][sharing-a-mutext-between-multiple-threads]. ### Implementing `Send` and `Sync` Manually Is Unsafe diff --git a/src/ch18-01-what-is-oo.md b/src/ch18-01-what-is-oo.md index e370757468..ed7dbb5523 100644 --- a/src/ch18-01-what-is-oo.md +++ b/src/ch18-01-what-is-oo.md @@ -15,9 +15,9 @@ Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Addison-Wesley Professional, 1994), colloquially referred to as _The Gang of Four_ book, is a catalog of object-oriented design patterns. It defines OOP this way: -> Object-oriented programs are made up of objects. An _object_ packages both +> Object-oriented programs are made up of objects. An **object** packages both > data and the procedures that operate on that data. The procedures are -> typically called _methods_ or _operations_. +> typically called **methods** or **operations**. Using this definition, Rust is object-oriented: structs and enums have data, and `impl` blocks provide methods on structs and enums. Even though structs and @@ -140,8 +140,8 @@ than necessary. Subclasses shouldn’t always share all characteristics of their parent class but will do so with inheritance. This can make a program’s design less flexible. It also introduces the possibility of calling methods on subclasses that don’t make sense or that cause errors because the methods don’t -apply to the subclass. In addition, some languages will only allow single -inheritance (meaning a subclass can only inherit from one class), further +apply to the subclass. In addition, some languages will only allow _single +inheritance_ (meaning a subclass can only inherit from one class), further restricting the flexibility of a program’s design. For these reasons, Rust takes the different approach of using trait objects diff --git a/src/ch18-02-trait-objects.md b/src/ch18-02-trait-objects.md index db118ef223..c2fa2c621b 100644 --- a/src/ch18-02-trait-objects.md +++ b/src/ch18-02-trait-objects.md @@ -40,17 +40,17 @@ allow users to extend it with new types. To implement the behavior we want `gui` to have, we’ll define a trait named `Draw` that will have one method named `draw`. Then we can define a vector that -takes a _trait object_. A trait object points to both an instance of a type +takes a trait object. A _trait object_ points to both an instance of a type implementing our specified trait and a table used to look up trait methods on that type at runtime. We create a trait object by specifying some sort of -pointer, such as a `&` reference or a `Box` smart pointer, then the `dyn` +pointer, such as an `&` reference or a `Box` smart pointer, then the `dyn` keyword, and then specifying the relevant trait. (We’ll talk about the reason -trait objects must use a pointer in Chapter 20 in the section [“Dynamically -Sized Types and the `Sized` Trait.”][dynamically-sized]) We can -use trait objects in place of a generic or concrete type. Wherever we use a -trait object, Rust’s type system will ensure at compile time that any value -used in that context will implement the trait object’s trait. Consequently, we -don’t need to know all the possible types at compile time. +trait objects must use a pointer in [“Dynamically Sized Types and the `Sized` +Trait”][dynamically-sized] in Chapter 20.) We can use trait +objects in place of a generic or concrete type. Wherever we use a trait object, +Rust’s type system will ensure at compile time that any value used in that +context will implement the trait object’s trait. Consequently, we don’t need to +know all the possible types at compile time. We’ve mentioned that, in Rust, we refrain from calling structs and enums “objects” to distinguish them from other languages’ objects. In a struct or @@ -100,8 +100,8 @@ On the `Screen` struct, we’ll define a method named `run` that will call the This works differently from defining a struct that uses a generic type -parameter with trait bounds. A generic type parameter can only be substituted -with one concrete type at a time, whereas trait objects allow for multiple +parameter with trait bounds. A generic type parameter can be substituted with +only one concrete type at a time, whereas trait objects allow for multiple concrete types to fill in for the trait object at runtime. For example, we could have defined the `Screen` struct using a generic type and a trait bound as in Listing 18-6: @@ -221,9 +221,9 @@ didn’t mean to pass and so should pass a different type or we should implement ### Trait Objects Perform Dynamic Dispatch -Recall in the [“Performance of Code Using -Generics”][performance-of-code-using-generics] section in Chapter -10 our discussion on the monomorphization process performed on generics by the +Recall in [“Performance of Code Using +Generics”][performance-of-code-using-generics] in Chapter 10 our +discussion on the monomorphization process performed on generics by the compiler: the compiler generates nongeneric implementations of functions and methods for each concrete type that we use in place of a generic type parameter. The code that results from monomorphization is doing _static dispatch_, which is diff --git a/src/ch19-01-all-the-places-for-patterns.md b/src/ch19-01-all-the-places-for-patterns.md index 0db908a8a0..22d454d2bc 100644 --- a/src/ch19-01-all-the-places-for-patterns.md +++ b/src/ch19-01-all-the-places-for-patterns.md @@ -35,7 +35,7 @@ left of each arrow. One requirement for `match` expressions is that they need to be _exhaustive_ in the sense that all possibilities for the value in the `match` expression must be accounted for. One way to ensure you’ve covered every possibility is to have -a catchall pattern for the last arm: for example, a variable name matching any +a catch-all pattern for the last arm: for example, a variable name matching any value can never fail and thus covers every remaining case. The particular pattern `_` will match anything, but it never binds to a diff --git a/src/ch19-03-pattern-syntax.md b/src/ch19-03-pattern-syntax.md index d685f87bf6..ffb40dfcf8 100644 --- a/src/ch19-03-pattern-syntax.md +++ b/src/ch19-03-pattern-syntax.md @@ -258,7 +258,7 @@ as the value from each field in a struct, separately from each other. ### Ignoring Values in a Pattern You’ve seen that it’s sometimes useful to ignore values in a pattern, such as -in the last arm of a `match`, to get a catchall that doesn’t actually do +in the last arm of a `match`, to get a catch-all that doesn’t actually do anything but does account for all remaining possible values. There are a few ways to ignore entire values or parts of values in a pattern: using the `_` pattern (which you’ve seen), using the `_` pattern within another pattern, @@ -590,7 +590,7 @@ Rust’s patterns are very useful in distinguishing between different kinds of data. When used in `match` expressions, Rust ensures your patterns cover every possible value, or your program won’t compile. Patterns in `let` statements and function parameters make those constructs more useful, enabling the -destructuring of values into smaller parts at the same time as assigning to +destructuring of values into smaller parts and assigning those parts to variables. We can create simple or complex patterns to suit our needs. Next, for the penultimate chapter of the book, we’ll look at some advanced diff --git a/src/ch20-01-unsafe-rust.md b/src/ch20-01-unsafe-rust.md index 33e7c5966e..51938b3cc0 100644 --- a/src/ch20-01-unsafe-rust.md +++ b/src/ch20-01-unsafe-rust.md @@ -67,14 +67,13 @@ some abstractions that provide a safe interface to unsafe code. ### Dereferencing a Raw Pointer -In Chapter 4, in the [“Dangling References”][dangling-references] section, we mentioned that the compiler ensures references are always -valid. Unsafe Rust has two new types called _raw pointers_ that are similar to -references. As with references, raw pointers can be immutable or mutable and -are written as `*const T` and `*mut T`, respectively. The asterisk isn’t the -dereference operator; it’s part of the type name. In the context of raw -pointers, _immutable_ means that the pointer can’t be directly assigned to -after being dereferenced. +In Chapter 4, in [“Dangling References”][dangling-references], we +mentioned that the compiler ensures references are always valid. Unsafe Rust has +two new types called _raw pointers_ that are similar to references. As with +references, raw pointers can be immutable or mutable and are written as `*const +T` and `*mut T`, respectively. The asterisk isn’t the dereference operator; it’s +part of the type name. In the context of raw pointers, _immutable_ means that +the pointer can’t be directly assigned to after being dereferenced. Different from references and smart pointers, raw pointers: @@ -256,12 +255,12 @@ to unsafe functions to make the implementation of `split_at_mut` work. -Recall from [“The Slice Type”][the-slice-type] section in -Chapter 4 that slices are a pointer to some data and the length of the slice. -We use the `len` method to get the length of a slice and the `as_mut_ptr` -method to access the raw pointer of a slice. In this case, because we have a -mutable slice to `i32` values, `as_mut_ptr` returns a raw pointer with the type -`*mut i32`, which we’ve stored in the variable `ptr`. +Recall from [“The Slice Type”][the-slice-type] in Chapter 4 that +slices are a pointer to some data and the length of the slice. We use the `len` +method to get the length of a slice and the `as_mut_ptr` method to access the +raw pointer of a slice. In this case, because we have a mutable slice to `i32` +values, `as_mut_ptr` returns a raw pointer with the type `*mut i32`, which we’ve +stored in the variable `ptr`. We keep the assertion that the `mid` index is within the slice. Then we get to the unsafe code: the `slice::from_raw_parts_mut` function takes a raw pointer @@ -396,9 +395,9 @@ value. -Static variables are similar to constants, which we discussed in the -[“Constants”][differences-between-variables-and-constants] section -in Chapter 3. The names of static variables are in `SCREAMING_SNAKE_CASE` by +Static variables are similar to constants, which we discussed in +[“Constants”][differences-between-variables-and-constants] in +Chapter 3. The names of static variables are in `SCREAMING_SNAKE_CASE` by convention. Static variables can only store references with the `'static` lifetime, which means the Rust compiler can figure out the lifetime and we aren’t required to annotate it explicitly. Accessing an immutable static @@ -467,16 +466,16 @@ Listing 20-12. By using `unsafe impl`, we’re promising that we’ll uphold the invariants that the compiler can’t verify. -As an example, recall the `Sync` and `Send` marker traits we discussed in the +As an example, recall the `Sync` and `Send` marker traits we discussed in [“Extensible Concurrency with the `Sync` and `Send` -Traits”][extensible-concurrency-with-the-sync-and-send-traits] -section in Chapter 16: the compiler implements these traits automatically if -our types are composed entirely of `Send` and `Sync` types. If we implement a -type that contains a type that is not `Send` or `Sync`, such as raw pointers, -and we want to mark that type as `Send` or `Sync`, we must use `unsafe`. Rust -can’t verify that our type upholds the guarantees that it can be safely sent -across threads or accessed from multiple threads; therefore, we need to do -those checks manually and indicate as such with `unsafe`. +Traits”][extensible-concurrency-with-the-sync-and-send-traits] in +Chapter 16: the compiler implements these traits automatically if our types are +composed entirely of `Send` and `Sync` types. If we implement a type that +contains a type that is not `Send` or `Sync`, such as raw pointers, and we want +to mark that type as `Send` or `Sync`, we must use `unsafe`. Rust can’t verify +that our type upholds the guarantees that it can be safely sent across threads +or accessed from multiple threads; therefore, we need to do those checks +manually and indicate as such with `unsafe`. ### Accessing Fields of a Union diff --git a/src/ch20-02-advanced-traits.md b/src/ch20-02-advanced-traits.md index ebb9fff0a8..b66d550dfc 100644 --- a/src/ch20-02-advanced-traits.md +++ b/src/ch20-02-advanced-traits.md @@ -1,9 +1,9 @@ ## Advanced Traits -We first covered traits in the [“Traits: Defining Shared -Behavior”][traits-defining-shared-behavior] section of Chapter -10, but we didn’t discuss the more advanced details. Now that you know more -about Rust, we can get into the nitty-gritty. +We first covered traits in [“Traits: Defining Shared +Behavior”][traits-defining-shared-behavior] in Chapter 10, but we +didn’t discuss the more advanced details. Now that you know more about Rust, we +can get into the nitty-gritty. ### Specifying Placeholder Types in Trait Definitions with Associated Types @@ -160,8 +160,8 @@ value of the `Rhs` type parameter instead of using the default of `Self`. You’ll use default type parameters in two main ways: -- To extend a type without breaking existing code -- To allow customization in specific cases most users won’t need +1. To extend a type without breaking existing code +2. To allow customization in specific cases most users won’t need The standard library’s `Add` trait is an example of the second purpose: usually, you’ll add two like types, but the `Add` trait provides the ability to @@ -406,19 +406,18 @@ it within an outline of asterisks. ### Using the Newtype Pattern to Implement External Traits on External Types -In Chapter 10 in the [“Implementing a Trait on a -Type”][implementing-a-trait-on-a-type] section, we mentioned the -orphan rule that states we’re only allowed to implement a trait on a type if -either the trait or the type are local to our crate. It’s possible to get -around this restriction using the _newtype pattern_, which involves creating a -new type in a tuple struct. (We covered tuple structs in the [“Using Tuple -Structs without Named Fields to Create Different Types”][tuple-structs] section of Chapter 5.) The tuple struct will have one field and be a -thin wrapper around the type we want to implement a trait for. Then the wrapper -type is local to our crate, and we can implement the trait on the wrapper. -_Newtype_ is a term that originates from the Haskell programming language. -There is no runtime performance penalty for using this pattern, and the wrapper -type is elided at compile time. +In [“Implementing a Trait on a Type”][implementing-a-trait-on-a-type] in Chapter 10, we mentioned the orphan rule that states we’re only allowed +to implement a trait on a type if either the trait or the type are local to our +crate. It’s possible to get around this restriction using the _newtype pattern_, +which involves creating a new type in a tuple struct. (We covered tuple structs +in [“Using Tuple Structs without Named Fields to Create Different +Types”][tuple-structs] in Chapter 5.) The tuple struct will have +one field and be a thin wrapper around the type we want to implement a trait +for. Then the wrapper type is local to our crate, and we can implement the trait +on the wrapper. _Newtype_ is a term that originates from the Haskell programming +language. There is no runtime performance penalty for using this pattern, and +the wrapper type is elided at compile time. As an example, let’s say we want to implement `Display` on `Vec`, which the orphan rule prevents us from doing directly because the `Display` trait and the @@ -440,15 +439,15 @@ tuple. Then we can use the functionality of the `Display` trait on `Wrapper`. The downside of using this technique is that `Wrapper` is a new type, so it doesn’t have the methods of the value it’s holding. We would have to implement -all the methods of `Vec` directly on `Wrapper` such that the methods -delegate to `self.0`, which would allow us to treat `Wrapper` exactly like a -`Vec`. If we wanted the new type to have every method the inner type has, -implementing the `Deref` trait (discussed in Chapter 15 in the [“Treating Smart -Pointers Like Regular References with the `Deref` -Trait”][smart-pointer-deref] section) on the `Wrapper` to return -the inner type would be a solution. If we don’t want the `Wrapper` type to have -all the methods of the inner type—for example, to restrict the `Wrapper` type’s -behavior—we would have to implement just the methods we do want manually. +all the methods of `Vec` directly on `Wrapper` such that the methods delegate +to `self.0`, which would allow us to treat `Wrapper` exactly like a `Vec`. If +we wanted the new type to have every method the inner type has, implementing the +`Deref` trait (discussed in [“Treating Smart Pointers Like Regular References +with the `Deref` Trait”][smart-pointer-deref] in Chapter 15) on +the `Wrapper` to return the inner type would be a solution. If we don’t want the +`Wrapper` type to have all the methods of the inner type—for example, to +restrict the `Wrapper` type’s behavior—we would have to implement just the +methods we do want manually. This newtype pattern is also useful even when traits are not involved. Let’s switch focus and look at some advanced ways to interact with Rust’s type system. diff --git a/src/ch20-03-advanced-types.md b/src/ch20-03-advanced-types.md index b61bdf6e2f..88d9d307e1 100644 --- a/src/ch20-03-advanced-types.md +++ b/src/ch20-03-advanced-types.md @@ -8,18 +8,15 @@ the `!` type and dynamically sized types. ### Using the Newtype Pattern for Type Safety and Abstraction -> Note: This section assumes you’ve read the earlier section [“Using the -> Newtype Pattern to Implement External Traits on External -> Types.”][using-the-newtype-pattern] - -The newtype pattern is also useful for tasks beyond those we’ve discussed so -far, including statically enforcing that values are never confused and -indicating the units of a value. You saw an example of using newtypes to +This section assumes you’ve read the earlier section [“Using the Newtype Pattern +to Implement External Traits on External Types.”][using-the-newtype-pattern] The newtype pattern is also useful for tasks beyond those we’ve +discussed so far, including statically enforcing that values are never confused +and indicating the units of a value. You saw an example of using newtypes to indicate units in Listing 20-16: recall that the `Millimeters` and `Meters` structs wrapped `u32` values in a newtype. If we wrote a function with a -parameter of type `Millimeters`, we couldn’t compile a program that -accidentally tried to call that function with a value of type `Meters` or a -plain `u32`. +parameter of type `Millimeters`, we couldn’t compile a program that accidentally +tried to call that function with a value of type `Meters` or a plain `u32`. We can also use the newtype pattern to abstract away some implementation details of a type: the new type can expose a public API that is different from @@ -30,11 +27,10 @@ Newtypes can also hide internal implementation. For example, we could provide a associated with their name. Code using `People` would only interact with the public API we provide, such as a method to add a name string to the `People` collection; that code wouldn’t need to know that we assign an `i32` ID to names -internally. The newtype pattern is a lightweight way to achieve encapsulation -to hide implementation details, which we discussed in the [“Encapsulation that -Hides Implementation -Details”][encapsulation-that-hides-implementation-details] -section of Chapter 18. +internally. The newtype pattern is a lightweight way to achieve encapsulation to +hide implementation details, which we discussed in [“Encapsulation that Hides +Implementation Details”][encapsulation-that-hides-implementation-details] in Chapter 18. ### Creating Type Synonyms with Type Aliases @@ -85,7 +81,7 @@ A type alias makes this code more manageable by reducing the repetition. In Listing 20-26, we’ve introduced an alias named `Thunk` for the verbose type and can replace all uses of the type with the shorter alias `Thunk`. -+ ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-26/src/main.rs:here}} @@ -158,10 +154,10 @@ here in Listing 20-27. -At the time, we skipped over some details in this code. In Chapter 6 in [“The -`match` Control Flow Operator”][the-match-control-flow-operator] -section, we discussed that `match` arms must all return the same type. So, for -example, the following code doesn’t work: +At the time, we skipped over some details in this code. In [“The `match` Control +Flow Operator”][the-match-control-flow-operator] in Chapter 6, we +discussed that `match` arms must all return the same type. So, for example, the +following code doesn’t work: ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch20-advanced-features/no-listing-08-match-arms-different-types/src/main.rs:here}} @@ -233,28 +229,28 @@ storage and `s2` needs 15. This is why it’s not possible to create a variable holding a dynamically sized type. So what do we do? In this case, you already know the answer: we make the types -of `s1` and `s2` a `&str` rather than a `str`. Recall from the [“String -Slices”][string-slices] section of Chapter 4 that the slice data +of `s1` and `s2` a `&str` rather than a `str`. Recall from [“String +Slices”][string-slices] in Chapter 4 that the slice data structure just stores the starting position and the length of the slice. So although a `&T` is a single value that stores the memory address of where the `T` is located, a `&str` is _two_ values: the address of the `str` and its length. As such, we can know the size of a `&str` value at compile time: it’s twice the length of a `usize`. That is, we always know the size of a `&str`, no -matter how long the string it refers to is. In general, this is the way in -which dynamically sized types are used in Rust: they have an extra bit of -metadata that stores the size of the dynamic information. The golden rule of -dynamically sized types is that we must always put values of dynamically sized -types behind a pointer of some kind. +matter how long the string it refers to is. In general, this is the way in which +dynamically sized types are used in Rust: they have an extra bit of metadata +that stores the size of the dynamic information. The golden rule of dynamically +sized types is that we must always put values of dynamically sized types behind +a pointer of some kind. We can combine `str` with all kinds of pointers: for example, `Box` or `Rc`. In fact, you’ve seen this before but with a different dynamically sized type: traits. Every trait is a dynamically sized type we can refer to by -using the name of the trait. In Chapter 18 in the [“Using Trait Objects That -Allow for Values of Different -Types”][using-trait-objects-that-allow-for-values-of-different-types] section, we mentioned that to use traits as trait objects, we must -put them behind a pointer, such as `&dyn Trait` or `Box` (`Rc` would work too). +using the name of the trait. In [“Using Trait Objects That Allow for Values of +Different +Types”][using-trait-objects-that-allow-for-values-of-different-types] in Chapter 18, we mentioned that to use traits as trait objects, we must put +them behind a pointer, such as `&dyn Trait` or `Box` (`Rc` +would work too). To work with DSTs, Rust provides the `Sized` trait to determine whether or not a type’s size is known at compile time. This trait is automatically implemented diff --git a/src/ch20-04-advanced-functions-and-closures.md b/src/ch20-04-advanced-functions-and-closures.md index e8692eea93..6bde4058fa 100644 --- a/src/ch20-04-advanced-functions-and-closures.md +++ b/src/ch20-04-advanced-functions-and-closures.md @@ -65,17 +65,17 @@ like this: {{#rustdoc_include ../listings/ch20-advanced-features/no-listing-16-map-function/src/main.rs:here}} ``` -Note that we must use the fully qualified syntax that we talked about earlier -in the [“Advanced Traits”][advanced-traits] section because -there are multiple functions available named `to_string`. Here, we’re using the -`to_string` function defined in the `ToString` trait, which the standard -library has implemented for any type that implements `Display`. - -Recall from the [“Enum values”][enum-values] section of Chapter -6 that the name of each enum variant that we define also becomes an initializer -function. We can use these initializer functions as function pointers that -implement the closure traits, which means we can specify the initializer -functions as arguments for methods that take closures, like so: +Note that we must use the fully qualified syntax that we talked about in +[“Advanced Traits”][advanced-traits] because there are multiple +functions available named `to_string`. Here, we’re using the `to_string` +function defined in the `ToString` trait, which the standard library has +implemented for any type that implements `Display`. + +Recall from [“Enum values”][enum-values] in Chapter 6 that the +name of each enum variant that we define also becomes an initializer function. +We can use these initializer functions as function pointers that implement the +closure traits, which means we can specify the initializer functions as +arguments for methods that take closures, like so: ```rust {{#rustdoc_include ../listings/ch20-advanced-features/no-listing-17-map-initializer/src/main.rs:here}} diff --git a/src/ch20-05-macros.md b/src/ch20-05-macros.md index edc5fc4c8f..26e6941620 100644 --- a/src/ch20-05-macros.md +++ b/src/ch20-05-macros.md @@ -433,7 +433,7 @@ trait implementation. Next, let’s explore how the other kinds of procedural macros differ from custom derive macros. -### Attribute-like macros +### Attribute-Like macros Attribute-like macros are similar to custom derive macros, but instead of generating code for the `derive` attribute, they allow you to create new @@ -464,17 +464,17 @@ Other than that, attribute-like macros work the same way as custom derive macros: you create a crate with the `proc-macro` crate type and implement a function that generates the code you want! -### Function-like macros +### Function-Like macros Function-like macros define macros that look like function calls. Similarly to `macro_rules!` macros, they’re more flexible than functions; for example, they -can take an unknown number of arguments. However, `macro_rules!` macros can be -defined only using the match-like syntax we discussed in the section -[“Declarative Macros with `macro_rules!` for General -Metaprogramming”][decl] earlier. Function-like macros take a -`TokenStream` parameter and their definition manipulates that `TokenStream` -using Rust code as the other two types of procedural macros do. An example of a -function-like macro is an `sql!` macro that might be called like so: +can take an unknown number of arguments. However, `macro_rules!` macros can only +be defined using the match-like syntax we discussed in [“Declarative Macros with +`macro_rules!` for General Metaprogramming”][decl] earlier. +Function-like macros take a `TokenStream` parameter and their definition +manipulates that `TokenStream` using Rust code as the other two types of +procedural macros do. An example of a function-like macro is an `sql!` macro +that might be called like so: ```rust,ignore let sql = sql!(SELECT * FROM posts WHERE id=1); diff --git a/src/ch21-01-single-threaded.md b/src/ch21-01-single-threaded.md index 2c35b25b59..7f12fa8669 100644 --- a/src/ch21-01-single-threaded.md +++ b/src/ch21-01-single-threaded.md @@ -57,7 +57,7 @@ to a port.” The `bind` function returns a `Result`, which indicates that it’s possible for binding to fail. For example, connecting to port 80 requires -administrator privileges (nonadministrators can listen only on ports higher +administrator privileges (non-administrators can listen only on ports higher than 1023), so if we tried to connect to port 80 without being an administrator, binding wouldn’t work. Binding also wouldn’t work, for example, if we ran two instances of our program and so had two programs listening to the @@ -89,7 +89,7 @@ connections are closed. Let’s try running this code! Invoke `cargo run` in the terminal and then load _127.0.0.1:7878_ in a web browser. The browser should show an error message -like “Connection reset,” because the server isn’t currently sending back any +like “Connection reset” because the server isn’t currently sending back any data. But when you look at your terminal, you should see several messages that were printed when the browser connected to the server! @@ -100,7 +100,7 @@ Connection established! Connection established! ``` -Sometimes, you’ll see multiple messages printed for one browser request; the +Sometimes you’ll see multiple messages printed for one browser request; the reason might be that the browser is making a request for the page as well as a request for other resources, like the _favicon.ico_ icon that appears in the browser tab. @@ -216,17 +216,17 @@ being used, such as `GET` or `POST`, which describes how the client is making this request. Our client used a `GET` request, which means it is asking for information. -The next part of the request line is _/_, which indicates the _Uniform Resource -Identifier_ _(URI)_ the client is requesting: a URI is almost, but not quite, -the same as a _Uniform Resource Locator_ _(URL)_. The difference between URIs +The next part of the request line is _/_, which indicates the _uniform resource +identifier_ _(URI)_ the client is requesting: a URI is almost, but not quite, +the same as a _uniform resource locator_ _(URL)_. The difference between URIs and URLs isn’t important for our purposes in this chapter, but the HTTP spec -uses the term URI, so we can just mentally substitute URL for URI here. +uses the term URI, so we can just mentally substitute _URL_ for _URI_ here. The last part is the HTTP version the client uses, and then the request line -ends in a _CRLF sequence_. (CRLF stands for _carriage return_ and _line feed_, +ends in a CRLF sequence. (CRLF stands for _carriage return_ and _line feed_, which are terms from the typewriter days!) The CRLF sequence can also be written as `\r\n`, where `\r` is a carriage return and `\n` is a line feed. The -CRLF sequence separates the request line from the rest of the request data. +_CRLF sequence_ separates the request line from the rest of the request data. Note that when the CRLF is printed, we see a new line start rather than `\r\n`. Looking at the request line data we received from running our program so far, @@ -258,7 +258,7 @@ a reason phrase that provides a text description of the status code. After the CRLF sequence are any headers, another CRLF sequence, and the body of the response. -Here is an example response that uses HTTP version 1.1, has a status code of +Here is an example response that uses HTTP version 1.1, and has a status code of 200, an OK reason phrase, no headers, and no body: ```text @@ -289,7 +289,7 @@ application you would add error handling here. With these changes, let’s run our code and make a request. We’re no longer printing any data to the terminal, so we won’t see any output other than the output from Cargo. When you load _127.0.0.1:7878_ in a web browser, you should -get a blank page instead of an error. You’ve just hand-coded receiving an HTTP +get a blank page instead of an error. You’ve just handcoded receiving an HTTP request and sending a response! ### Returning Real HTML @@ -322,8 +322,8 @@ and send it. We’ve added `fs` to the `use` statement to bring the standard library’s filesystem module into scope. The code for reading the contents of a file to a -string should look familiar; we used it in Chapter 12 when we read the contents -of a file for our I/O project in Listing 12-4. +string should look familiar; we used it when we read the contents of a file for +our I/O project in Listing 12-4. Next, we use `format!` to add the file’s contents as the body of the success response. To ensure a valid HTTP response, we add the `Content-Length` header @@ -412,14 +412,14 @@ _127.0.0.1:7878/foo_, should return the error HTML from _404.html_. ### A Touch of Refactoring -At the moment the `if` and `else` blocks have a lot of repetition: they’re both +At the moment, the `if` and `else` blocks have a lot of repetition: they’re both reading files and writing the contents of the files to the stream. The only differences are the status line and the filename. Let’s make the code more concise by pulling out those differences into separate `if` and `else` lines -that will assign the values of the status line and the filename to variables; -we can then use those variables unconditionally in the code to read the file -and write the response. Listing 21-9 shows the resulting code after replacing -the large `if` and `else` blocks. +that will assign the values of the status line and the filename to variables; we +can then use those variables unconditionally in the code to read the file and +write the response. Listing 21-9 shows the resultant code after replacing the +large `if` and `else` blocks. diff --git a/src/ch21-02-multithreaded.md b/src/ch21-02-multithreaded.md index 0a5ef9f15e..51a6bab722 100644 --- a/src/ch21-02-multithreaded.md +++ b/src/ch21-02-multithreaded.md @@ -6,14 +6,14 @@ server received more and more requests, this serial execution would be less and less optimal. If the server receives a request that takes a long time to process, subsequent requests will have to wait until the long request is finished, even if the new requests can be processed quickly. We’ll need to fix -this, but first, we’ll look at the problem in action. +this, but first we’ll look at the problem in action. ### Simulating a Slow Request in the Current Server Implementation We’ll look at how a slow-processing request can affect other requests made to our current server implementation. Listing 21-10 implements handling a request to _/sleep_ with a simulated slow response that will cause the server to sleep -for 5 seconds before responding. +for five seconds before responding. @@ -26,12 +26,12 @@ for 5 seconds before responding. We switched from `if` to `match` now that we have three cases. We need to explicitly match on a slice of `request_line` to pattern match against the string literal values; `match` doesn’t do automatic referencing and -dereferencing like the equality method does. +dereferencing, like the equality method does. The first arm is the same as the `if` block from Listing 21-9. The second arm matches a request to _/sleep_. When that request is received, the server will -sleep for 5 seconds before rendering the successful HTML page. The third arm is -the same as the `else` block from Listing 21-9. +sleep for five seconds before rendering the successful HTML page. The third arm +is the same as the `else` block from Listing 21-9. You can see how primitive our server is: real libraries would handle the recognition of multiple requests in a much less verbose way! @@ -40,7 +40,7 @@ Start the server using `cargo run`. Then open two browser windows: one for _http://127.0.0.1:7878/_ and the other for _http://127.0.0.1:7878/sleep_. If you enter the _/_ URI a few times, as before, you’ll see it respond quickly. But if you enter _/sleep_ and then load _/_, you’ll see that _/_ waits until -`sleep` has slept for its full 5 seconds before loading. +`sleep` has slept for its full five seconds before loading. There are multiple techniques we could use to avoid requests backing up behind a slow request, including using async as we did Chapter 17; the one we’ll @@ -58,24 +58,24 @@ a new task. A thread pool allows you to process connections concurrently, increasing the throughput of your server. We’ll limit the number of threads in the pool to a small number to protect us -from Denial of Service (DoS) attacks; if we had our program create a new thread -for each request as it came in, someone making 10 million requests to our -server could create havoc by using up all our server’s resources and grinding -the processing of requests to a halt. +from DoS attacks; if we had our program create a new thread for each request as +it came in, someone making 10 million requests to our server could create havoc +by using up all our server’s resources and grinding the processing of requests +to a halt. Rather than spawning unlimited threads, then, we’ll have a fixed number of threads waiting in the pool. Requests that come in are sent to the pool for processing. The pool will maintain a queue of incoming requests. Each of the threads in the pool will pop off a request from this queue, handle the request, and then ask the queue for another request. With this design, we can process up -to `N` requests concurrently, where `N` is the number of threads. If each +to *`N`* requests concurrently, where *`N`* is the number of threads. If each thread is responding to a long-running request, subsequent requests can still back up in the queue, but we’ve increased the number of long-running requests we can handle before reaching that point. This technique is just one of many ways to improve the throughput of a web -server. Other options you might explore are the _fork/join model_, the -_single-threaded async I/O model_, or the _multi-threaded async I/O model_. If +server. Other options you might explore are the fork/join model, the +single-threaded async I/O model, and the multi-threaded async I/O model. If you’re interested in this topic, you can read more about other solutions and try to implement them; with a low-level language like Rust, all of these options are possible. @@ -132,9 +132,9 @@ pool and think about how things would look different or the same with async. #### Creating a Finite Number of Threads -We want our thread pool to work in a similar, familiar way so switching from -threads to a thread pool doesn’t require large changes to the code that uses -our API. Listing 21-12 shows the hypothetical interface for a `ThreadPool` +We want our thread pool to work in a similar, familiar way so that switching +from threads to a thread pool doesn’t require large changes to the code that +uses our API. Listing 21-12 shows the hypothetical interface for a `ThreadPool` struct we want to use instead of `thread::spawn`. @@ -168,13 +168,13 @@ error we get: Great! This error tells us we need a `ThreadPool` type or module, so we’ll build one now. Our `ThreadPool` implementation will be independent of the kind -of work our web server is doing. So, let’s switch the `hello` crate from a +of work our web server is doing. So let’s switch the `hello` crate from a binary crate to a library crate to hold our `ThreadPool` implementation. After we change to a library crate, we could also use the separate thread pool library for any work we want to do using a thread pool, not just for serving web requests. -Create a _src/lib.rs_ that contains the following, which is the simplest +Create a _src/lib.rs_ file that contains the following, which is the simplest definition of a `ThreadPool` struct that we can have for now: @@ -217,11 +217,11 @@ characteristics: -We chose `usize` as the type of the `size` parameter, because we know that a +We chose `usize` as the type of the `size` parameter because we know that a negative number of threads doesn’t make any sense. We also know we’ll use this -4 as the number of elements in a collection of threads, which is what the -`usize` type is for, as discussed in the [“Integer Types”][integer-types] section of Chapter 3. +`4` as the number of elements in a collection of threads, which is what the +`usize` type is for, as discussed in [“Integer Types”][integer-types] in Chapter 3. Let’s check the code again: @@ -230,20 +230,20 @@ Let’s check the code again: ``` Now the error occurs because we don’t have an `execute` method on `ThreadPool`. -Recall from the [“Creating a Finite Number of -Threads”](#creating-a-finite-number-of-threads) section that we -decided our thread pool should have an interface similar to `thread::spawn`. In +Recall from [“Creating a Finite Number of +Threads”](#creating-a-finite-number-of-threads) that we decided +our thread pool should have an interface similar to `thread::spawn`. In addition, we’ll implement the `execute` function so it takes the closure it’s given and gives it to an idle thread in the pool to run. We’ll define the `execute` method on `ThreadPool` to take a closure as a -parameter. Recall from the [“Moving Captured Values Out of the Closure and the -`Fn` Traits”][fn-traits] section in Chapter 13 that we can take -closures as parameters with three different traits: `Fn`, `FnMut`, and -`FnOnce`. We need to decide which kind of closure to use here. We know we’ll -end up doing something similar to the standard library `thread::spawn` -implementation, so we can look at what bounds the signature of `thread::spawn` -has on its parameter. The documentation shows us the following: +parameter. Recall from [“Moving Captured Values Out of the Closure and the `Fn` +Traits”][fn-traits] in Chapter 13 that we can take closures as +parameters with three different traits: `Fn`, `FnMut`, and `FnOnce`. We need to +decide which kind of closure to use here. We know we’ll end up doing something +similar to the standard library `thread::spawn` implementation, so we can look +at what bounds the signature of `thread::spawn` has on its parameter. The +documentation shows us the following: ```rust,ignore pub fn spawn(f: F) -> JoinHandle @@ -281,7 +281,7 @@ definitions, the return type can be omitted from the signature, but even if we have no parameters, we still need the parentheses. Again, this is the simplest implementation of the `execute` method: it does -nothing, but we’re trying only to make our code compile. Let’s check it again: +nothing, but we’re only trying to make our code compile. Let’s check it again: ```console {{#include ../listings/ch21-web-server/no-listing-03-define-execute/output.txt}} @@ -307,7 +307,7 @@ instead of a closure? We aren’t doing anything with the parameters to `new` and `execute`. Let’s implement the bodies of these functions with the behavior we want. To start, let’s think about `new`. Earlier we chose an unsigned type for the `size` -parameter, because a pool with a negative number of threads makes no sense. +parameter because a pool with a negative number of threads makes no sense. However, a pool with zero threads also makes no sense, yet zero is a perfectly valid `usize`. We’ll add code to check that `size` is greater than zero before we return a `ThreadPool` instance and have the program panic if it receives a @@ -372,13 +372,13 @@ returned a `ThreadPool` instance containing them. -We’ve brought `std::thread` into scope in the library crate, because we’re +We’ve brought `std::thread` into scope in the library crate because we’re using `thread::JoinHandle` as the type of the items in the vector in `ThreadPool`. Once a valid size is received, our `ThreadPool` creates a new vector that can hold `size` items. The `with_capacity` function performs the same task as -`Vec::new` but with an important difference: it preallocates space in the +`Vec::new` but with an important difference: it pre-allocates space in the vector. Because we know we need to store `size` elements in the vector, doing this allocation up front is slightly more efficient than using `Vec::new`, which resizes itself as elements are inserted. @@ -399,17 +399,19 @@ implement it manually. We’ll implement this behavior by introducing a new data structure between the `ThreadPool` and the threads that will manage this new behavior. We’ll call this data structure _Worker_, which is a common term in pooling -implementations. The Worker picks up code that needs to be run and runs the -code in the Worker’s thread. Think of people working in the kitchen at a -restaurant: the workers wait until orders come in from customers, and then -they’re responsible for taking those orders and fulfilling them. +implementations. The `Worker` picks up code that needs to be run and runs the +code in the Worker’s thread. + +Think of people working in the kitchen at a restaurant: the workers wait until +orders come in from customers, and then they’re responsible for taking those +orders and fulfilling them. Instead of storing a vector of `JoinHandle<()>` instances in the thread pool, we’ll store instances of the `Worker` struct. Each `Worker` will store a single `JoinHandle<()>` instance. Then we’ll implement a method on `Worker` that will take a closure of code to run and send it to the already running thread for -execution. We’ll also give each worker an `id` so we can distinguish between -the different workers in the pool when logging or debugging. +execution. We’ll also give each `Worker` an `id` so we can distinguish between +the different instances of `Worker` in the pool when logging or debugging. Here is the new process that will happen when we create a `ThreadPool`. We’ll implement the code that sends the closure to the thread after we have `Worker` @@ -499,12 +501,12 @@ the channel. In `ThreadPool::new`, we create our new channel and have the pool hold the sender. This will successfully compile. -Let’s try passing a receiver of the channel into each worker as the thread pool -creates the channel. We know we want to use the receiver in the thread that the -workers spawn, so we’ll reference the `receiver` parameter in the closure. The -code in Listing 21-17 won’t quite compile yet. +Let’s try passing a receiver of the channel into each `Worker` as the thread +pool creates the channel. We know we want to use the receiver in the thread that +the `Worker` instances spawn, so we’ll reference the `receiver` parameter in the +closure. The code in Listing 21-17 won’t quite compile yet. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch21-web-server/listing-21-17/src/lib.rs:here}} @@ -526,7 +528,8 @@ won’t work, as you’ll recall from Chapter 16: the channel implementation tha Rust provides is multiple _producer_, single _consumer_. This means we can’t just clone the consuming end of the channel to fix this code. We also don’t want to send a message multiple times to multiple consumers; we want one list -of messages with multiple workers such that each message gets processed once. +of messages with multiple `Worker` instances such that each message gets +processed once. Additionally, taking a job off the channel queue involves mutating the `receiver`, so the threads need a safe way to share and modify `receiver`; @@ -534,11 +537,11 @@ otherwise, we might get race conditions (as covered in Chapter 16). Recall the thread-safe smart pointers discussed in Chapter 16: to share ownership across multiple threads and allow the threads to mutate the value, we -need to use `Arc>`. The `Arc` type will let multiple workers own the -receiver, and `Mutex` will ensure that only one worker gets a job from the -receiver at a time. Listing 21-18 shows the changes we need to make. +need to use `Arc>`. The `Arc` type will let multiple `Worker` instances +own the receiver, and `Mutex` will ensure that only one `Worker` gets a job from +the receiver at a time. Listing 21-18 shows the changes we need to make. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch21-web-server/listing-21-18/src/lib.rs:here}} @@ -547,8 +550,8 @@ receiver at a time. Listing 21-18 shows the changes we need to make. In `ThreadPool::new`, we put the receiver in an `Arc` and a `Mutex`. For each -new worker, we clone the `Arc` to bump the reference count so the workers can -share ownership of the receiver. +new `Worker`, we clone the `Arc` to bump the reference count so the `Worker` +instances can share ownership of the receiver. With these changes, the code compiles! We’re getting there! @@ -556,10 +559,10 @@ With these changes, the code compiles! We’re getting there! Let’s finally implement the `execute` method on `ThreadPool`. We’ll also change `Job` from a struct to a type alias for a trait object that holds the type of -closure that `execute` receives. As discussed in the [“Creating Type Synonyms -with Type Aliases”][creating-type-synonyms-with-type-aliases] -section of Chapter 20, type aliases allow us to make long types shorter for -ease of use. Look at Listing 21-19. +closure that `execute` receives. As discussed in [“Creating Type Synonyms with +Type Aliases”][creating-type-synonyms-with-type-aliases] in +Chapter 20, type aliases allow us to make long types shorter for ease of use. +Look at Listing 21-19. @@ -578,13 +581,13 @@ executing: our threads continue executing as long as the pool exists. The reason we use `unwrap` is that we know the failure case won’t happen, but the compiler doesn’t know that. -But we’re not quite done yet! In the worker, our closure being passed to +But we’re not quite done yet! In the `Worker`, our closure being passed to `thread::spawn` still only _references_ the receiving end of the channel. Instead, we need the closure to loop forever, asking the receiving end of the channel for a job and running the job when it gets one. Let’s make the change shown in Listing 21-20 to `Worker::new`. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch21-web-server/listing-21-20/src/lib.rs:here}} @@ -664,7 +667,7 @@ _/sleep_, the server will be able to serve other requests by having another thread run them. > Note: If you open _/sleep_ in multiple browser windows simultaneously, they -> might load one at a time in 5 second intervals. Some web browsers execute +> might load one at a time in five-second intervals. Some web browsers execute > multiple instances of the same request sequentially for caching reasons. This > limitation is not caused by our web server. @@ -697,11 +700,11 @@ longer than intended if we aren’t mindful of the lifetime of the The code in Listing 21-20 that uses `let job = receiver.lock().unwrap().recv().unwrap();` works because with `let`, any -temporary values used in the expression on the right hand side of the equals +temporary values used in the expression on the right hand side of the equal sign are immediately dropped when the `let` statement ends. However, `while let` (and `if let` and `match`) does not drop temporary values until the end of the associated block. In Listing 21-21, the lock remains held for the duration -of the call to `job()`, meaning other workers cannot receive jobs. +of the call to `job()`, meaning other `Worker` instances cannot receive jobs. [creating-type-synonyms-with-type-aliases]: ch20-03-advanced-types.html#creating-type-synonyms-with-type-aliases [integer-types]: ch03-02-data-types.html#integer-types diff --git a/src/ch21-03-graceful-shutdown-and-cleanup.md b/src/ch21-03-graceful-shutdown-and-cleanup.md index 3158fba0cd..14938d2ae6 100644 --- a/src/ch21-03-graceful-shutdown-and-cleanup.md +++ b/src/ch21-03-graceful-shutdown-and-cleanup.md @@ -34,12 +34,12 @@ quite work yet. -First, we loop through each of the thread pool `workers`. We use `&mut` for -this because `self` is a mutable reference, and we also need to be able to -mutate `worker`. For each worker, we print a message saying that this -particular worker is shutting down, and then we call `join` on that worker’s -thread. If the call to `join` fails, we use `unwrap` to make Rust panic and go -into an ungraceful shutdown. +First, we loop through each of the thread pool `workers`. We use `&mut` for this +because `self` is a mutable reference, and we also need to be able to mutate +`worker`. For each worker, we print a message saying that this particular +`Worker` instance is shutting down, and then we call `join` on that `Worker` +instance’s thread. If the call to `join` fails, we use `unwrap` to make Rust +panic and go into an ungraceful shutdown. Here is the error we get when we compile this code: @@ -55,17 +55,17 @@ we did in Listing 18-15. If `Worker` held an `Option>`, we could call the `take` method on the `Option` to move the value out of the `Some` variant and leave a `None` variant in its place. In other words, a `Worker` that is running would have a `Some` variant in `thread`, and when we -wanted to clean up a `Worker`, we would replace `Some` with `None` so the -`Worker` doesn’t have a thread to run. +wanted to clean up a `Worker`, we’d replace `Some` with `None` so the `Worker` +wouldn’t have a thread to run. However, the _only_ time this would come up would be when dropping the `Worker`. -In exchange, we would have to deal with an `Option>` -everywhere we access `worker.thread`. Idiomatic Rust uses `Option` quite a bit, -but when you find yourself wrapping something in `Option` as a workaround even -though you know the item will always be present, it is a good idea to look for -alternative approaches. They can make your code cleaner and less error-prone. +In exchange, we’d have to deal with an `Option>` anywhere +we accessed `worker.thread`. Idiomatic Rust uses `Option` quite a bit, but when +you find yourself wrapping something you know will always be present in `Option` +as a workaround like this, it’s a good idea to look for alternative approaches. +They can make your code cleaner and less error-prone. -In this case, there is a better alternative: the `Vec::drain` method. It accepts +In this case, a better alternative exists: the `Vec::drain` method. It accepts a range parameter to specify which items to remove from the `Vec`, and returns an iterator of those items. Passing the `..` range syntax will remove _every_ value from the `Vec`. @@ -86,23 +86,23 @@ code. ### Signaling to the Threads to Stop Listening for Jobs With all the changes we’ve made, our code compiles without any warnings. -However, the bad news is this code doesn’t function the way we want it to yet. -The key is the logic in the closures run by the threads of the `Worker` +However, the bad news is that this code doesn’t function the way we want it to +yet. The key is the logic in the closures run by the threads of the `Worker` instances: at the moment, we call `join`, but that won’t shut down the threads -because they `loop` forever looking for jobs. If we try to drop our -`ThreadPool` with our current implementation of `drop`, the main thread will -block forever waiting for the first thread to finish. +because they `loop` forever looking for jobs. If we try to drop our `ThreadPool` +with our current implementation of `drop`, the main thread will block forever, +waiting for the first thread to finish. To fix this problem, we’ll need a change in the `ThreadPool` `drop` implementation and then a change in the `Worker` loop. -First, we’ll change the `ThreadPool` `drop` implementation to explicitly drop +First we’ll change the `ThreadPool` `drop` implementation to explicitly drop the `sender` before waiting for the threads to finish. Listing 21-23 shows the -changes to `ThreadPool` to explicitly drop `sender`. Unlike with the `workers`, +changes to `ThreadPool` to explicitly drop `sender`. Unlike with the thread, here we _do_ need to use an `Option` to be able to move `sender` out of `ThreadPool` with `Option::take`. -+ ```rust,noplayground,not_desired_behavior {{#rustdoc_include ../listings/ch21-web-server/listing-21-23/src/lib.rs:here}} @@ -111,12 +111,12 @@ here we _do_ need to use an `Option` to be able to move `sender` out of Dropping `sender` closes the channel, which indicates no more messages will be -sent. When that happens, all the calls to `recv` that the workers do in the -infinite loop will return an error. In Listing 21-24, we change the `Worker` -loop to gracefully exit the loop in that case, which means the threads will -finish when the `ThreadPool` `drop` implementation calls `join` on them. +sent. When that happens, all the calls to `recv` that the `Worker` instances do +in the infinite loop will return an error. In Listing 21-24, we change the +`Worker` loop to gracefully exit the loop in that case, which means the threads +will finish when the `ThreadPool` `drop` implementation calls `join` on them. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch21-web-server/listing-21-24/src/lib.rs:here}} @@ -127,7 +127,7 @@ finish when the `ThreadPool` `drop` implementation calls `join` on them. To see this code in action, let’s modify `main` to accept only two requests before gracefully shutting down the server, as shown in Listing 21-25. -+ ```rust,ignore {{#rustdoc_include ../listings/ch21-web-server/listing-21-25/src/main.rs:here}} @@ -175,21 +175,22 @@ Shutting down worker 2 Shutting down worker 3 ``` -You might see a different ordering of workers and messages printed. We can see -how this code works from the messages: workers 0 and 3 got the first two -requests. The server stopped accepting connections after the second connection, -and the `Drop` implementation on `ThreadPool` starts executing before worker 3 -even starts its job. Dropping the `sender` disconnects all the workers and -tells them to shut down. The workers each print a message when they disconnect, -and then the thread pool calls `join` to wait for each worker thread to finish. +You might see a different ordering of `Worker` IDs and messages printed. We can +see how this code works from the messages: `Worker` instances 0 and 3 got the +first two requests. The server stopped accepting connections after the second +connection, and the `Drop` implementation on `ThreadPool` starts executing +before `Worker` 3 even starts its job. Dropping the `sender` disconnects all the +`Worker` instances and tells them to shut down. The `Worker` instances each +print a message when they disconnect, and then the thread pool calls `join` to +wait for each `Worker` thread to finish. Notice one interesting aspect of this particular execution: the `ThreadPool` -dropped the `sender`, and before any worker received an error, we tried to join -worker 0. Worker 0 had not yet gotten an error from `recv`, so the main thread -blocked waiting for worker 0 to finish. In the meantime, worker 3 received a -job and then all threads received an error. When worker 0 finished, the main -thread waited for the rest of the workers to finish. At that point, they had -all exited their loops and stopped. +dropped the `sender`, and before any `Worker` received an error, we tried to +join `Worker` 0. `Worker` 0 had not yet gotten an error from `recv`, so the main +thread blocked waiting for `Worker` 0 to finish. In the meantime, `Worker` 3 +received a job and then all threads received an error. When `Worker` 0 finished, +the main thread waited for the rest of the `Worker` instances to finish. At that +point, they had all exited their loops and stopped. Congrats! We’ve now completed our project; we have a basic web server that uses a thread pool to respond asynchronously. We’re able to perform a graceful @@ -228,6 +229,6 @@ some ideas: Well done! You’ve made it to the end of the book! We want to thank you for joining us on this tour of Rust. You’re now ready to implement your own Rust -projects and help with other peoples’ projects. Keep in mind that there is a +projects and help with other people’s projects. Keep in mind that there is a welcoming community of other Rustaceans who would love to help you with any challenges you encounter on your Rust journey. diff --git a/src/title-page.md b/src/title-page.md index 7301493201..8d492c7d1c 100644 --- a/src/title-page.md +++ b/src/title-page.md @@ -18,7 +18,6 @@ This text is available in [paperback and ebook format from No Starch Press][nsprust]. [install]: ch01-01-installation.html -[editions]: appendix-05-editions.html [nsprust]: https://nostarch.com/rust-programming-language-2nd-edition [translations]: appendix-06-translation.html diff --git a/tools/doc-to-md.sh b/tools/doc-to-md.sh index 2649460065..2b25f3d69f 100755 --- a/tools/doc-to-md.sh +++ b/tools/doc-to-md.sh @@ -22,6 +22,9 @@ directory, so all fixes need to be made in `/src/`. unzip -o "tmp/$filename.docx" -d "tmp/$filename" # Convert to markdown with XSL. xsltproc tools/docx-to-md.xsl "tmp/$filename/word/document.xml" | \ + # Remove all non-breaking spaces, which NoStarch adds for inline references + # to listings, chapters, etc., but which we don’t care about in the source. + sed "s: : :g" | \ # Hard wrap at 80 chars at word boundaries. fold -w 80 -s | \ # Remove trailing whitespace and append to the file in the `nostarch` dir for comparison. diff --git a/tools/docx-to-md.xsl b/tools/docx-to-md.xsl index 8deec85c92..9c0e55c5f4 100644 --- a/tools/docx-to-md.xsl +++ b/tools/docx-to-md.xsl @@ -25,8 +25,16 @@ + + + + + + + + @@ -130,7 +138,7 @@ - + Filename: @@ -223,22 +231,30 @@ - : - + + + + + - + Listing - - + : - + + + + + - + > @@ -249,11 +265,11 @@ > - + > - + > @@ -304,7 +320,11 @@ - : - + + + + + @@ -345,17 +365,17 @@ Unmatched: - + - + ` - + ` @@ -387,22 +407,22 @@ Unmatched: - + - + * - + - - + + * - + @@ -432,8 +452,8 @@ Unmatched: - - + + <sup>