From 786fbf17b4c2b97381b810f8605572a80550ac45 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Tue, 13 Sep 2022 12:54:09 -0400 Subject: [PATCH] Regenerate everything --- nostarch/appendix_a.md | 142 +++ nostarch/appendix_b.md | 242 ++++ nostarch/appendix_c.md | 184 +++ nostarch/appendix_d.md | 175 +++ nostarch/appendix_e.md | 66 + nostarch/chapter01.md | 9 +- nostarch/chapter02.md | 21 +- nostarch/chapter03.md | 24 +- nostarch/chapter05.md | 46 +- nostarch/chapter06.md | 18 +- nostarch/chapter07.md | 56 +- nostarch/chapter08.md | 62 +- nostarch/chapter09.md | 39 +- nostarch/chapter10.md | 79 +- nostarch/chapter11.md | 32 +- nostarch/chapter12.md | 62 +- nostarch/chapter14.md | 812 +------------ nostarch/chapter15.md | 1718 ++------------------------ nostarch/chapter16.md | 1226 +------------------ nostarch/chapter17.md | 1021 +--------------- nostarch/chapter18.md | 1056 ++-------------- nostarch/chapter19.md | 1627 ++----------------------- nostarch/chapter20.md | 2561 ++++----------------------------------- nostarch/frontmatter.md | 9 +- 24 files changed, 1648 insertions(+), 9639 deletions(-) create mode 100644 nostarch/appendix_a.md create mode 100644 nostarch/appendix_b.md create mode 100644 nostarch/appendix_c.md create mode 100644 nostarch/appendix_d.md create mode 100644 nostarch/appendix_e.md diff --git a/nostarch/appendix_a.md b/nostarch/appendix_a.md new file mode 100644 index 00000000..ca3883be --- /dev/null +++ b/nostarch/appendix_a.md @@ -0,0 +1,142 @@ + + +[TOC] + +## Appendix A: Keywords + +The following lists contain keywords that are reserved for current or future +use by the Rust language. As such, they cannot be used as identifiers (except +as raw identifiers, as we’ll discuss in “Raw Identifiers” on page XX). +*Identifiers* are names of functions, variables, parameters, struct fields, +modules, crates, constants, macros, static values, attributes, types, traits, +or lifetimes. + +## Keywords Currently in Use + +The following is a list of keywords currently in use, with their functionality +described. + +* **`as` **: perform primitive casting, disambiguate the specific trait +containing an item, or rename items in `use` statements +* **`async` **: return a `Future` instead of blocking the current thread +* **`await` **: suspend execution until the result of a `Future` is ready +* **`break` **: exit a loop immediately +* **`const` **: define constant items or constant raw pointers +* **`continue` **: continue to the next loop iteration +* **`crate` **: in a module path, refers to the crate root +* **`dyn` **: dynamic dispatch to a trait object +* **`else` **: fallback for `if` and `if let` control flow constructs +* **`enum` **: define an enumeration +* **`extern` **: link an external function or variable +* **`false` **: Boolean false literal +* **`fn` **: define a function or the function pointer type +* **`for` **: loop over items from an iterator, implement a trait, or specify a +higher-ranked lifetime +* **`if` **: branch based on the result of a conditional expression +* **`impl` **: implement inherent or trait functionality +* **`in` **: part of `for` loop syntax +* **`let` **: bind a variable +* **`loop` **: loop unconditionally +* **`match` **: match a value to patterns +* **`mod` **: define a module +* **`move` **: make a closure take ownership of all its captures +* **`mut` **: denote mutability in references, raw pointers, or pattern bindings +* **`pub` **: denote public visibility in struct fields, `impl` blocks, or +modules +* **`ref` **: bind by reference +* **`return` **: return from function +* **`Self` **: a type alias for the type we are defining or implementing +* **`self` **: method subject or current module +* **`static` **: global variable or lifetime lasting the entire program +execution +* **`struct` **: define a structure +* **`super` **: parent module of the current module +* **`trait` **: define a trait +* **`true` **: Boolean true literal +* **`type` **: define a type alias or associated type +* **`union` **: define a union; is a keyword only when used in a union +declaration +* **`unsafe` **: denote unsafe code, functions, traits, or implementations +* **`use` **: bring symbols into scope +* **`where` **: denote clauses that constrain a type +* **`while` **: loop conditionally based on the result of an expression + +## Keywords Reserved for Future Use + +The following keywords do not yet have any functionality but are reserved by +Rust for potential future use: + +* `abstract` +* `become` +* `box` +* `do` +* `final` +* `macro` +* `override` +* `priv` +* `try` +* `typeof` +* `unsized` +* `virtual` +* `yield` + +## Raw Identifiers + +*Raw identifiers* are the syntax that lets you use keywords where they wouldn’t +normally be allowed. You use a raw identifier by prefixing a keyword with `r#`. + +For example, `match` is a keyword. If you try to compile the following function +that uses `match` as its name: + +Filename: src/main.rs + +``` +fn match(needle: &str, haystack: &str) -> bool { + haystack.contains(needle) +} +``` + +you’ll get this error: + +``` +error: expected identifier, found keyword `match` + --> src/main.rs:4:4 + | +4 | fn match(needle: &str, haystack: &str) -> bool { + | ^^^^^ expected identifier, found keyword +``` + +The error shows that you can’t use the keyword `match` as the function +identifier. To use `match` as a function name, you need to use the raw +identifier syntax, like this: + +Filename: src/main.rs + +``` +fn r#match(needle: &str, haystack: &str) -> bool { + haystack.contains(needle) +} + +fn main() { + assert!(r#match("foo", "foobar")); +} +``` + +This code will compile without any errors. Note the `r#` prefix on the function +name in its definition as well as where the function is called in `main`. + +Raw identifiers allow you to use any word you choose as an identifier, even if +that word happens to be a reserved keyword. This gives us more freedom to +choose identifier names, as well as lets us integrate with programs written in +a language where these words aren’t keywords. In addition, raw identifiers +allow you to use libraries written in a different Rust edition than your crate +uses. For example, `try` isn’t a keyword in the 2015 edition but is in the 2018 +and 2021 editions. If you depend on a library that is written using the 2015 +edition and has a `try` function, you’ll need to use the raw identifier syntax, +`r#try` in this case, to call that function from your 2021 edition code. See +Appendix E for more information on editions. + diff --git a/nostarch/appendix_b.md b/nostarch/appendix_b.md new file mode 100644 index 00000000..6c642385 --- /dev/null +++ b/nostarch/appendix_b.md @@ -0,0 +1,242 @@ + + +[TOC] + +## Appendix B: Operators and Symbols + +This appendix contains a glossary of Rust’s syntax, including operators and +other symbols that appear by themselves or in the context of paths, generics, +trait bounds, macros, attributes, comments, tuples, and brackets. + +## Operators + +Table B-1 contains the operators in Rust, an example of how the operator would +appear in context, a short explanation, and whether that operator is +overloadable. If an operator is overloadable, the relevant trait to use to +overload that operator is listed. + +Table -1: Operators + +| Operator | Example | Explanation | Overloadable? | +|---|---|---|---| +| `!` | `ident!(...)`, `ident!{...}`, `ident![...]` | Macro expansion | | +| `!` | `!expr` | Bitwise or logical complement | `Not` | +| `!=` | `expr != expr` | Nonequality comparison | `PartialEq` | +| `% | `expr % expr` | Arithmetic remainder | `Rem` | +| `%=` | `var %= expr` | Arithmetic remainder and assignment | `RemAssign` | +| `& | `&expr`, `&mut expr` | Borrow | | +| `&` | `&type`, `&mut type`, `&'a type`, `&'a mut type` | Borrowed pointer +type | | +| `&` | `expr & expr` | Bitwise AND | `BitAnd` | +| `&=` | `var &= expr` | Bitwise AND and assignment | `BitAndAssign` | +| `&&` | `expr && expr` | Short-circuiting logical AND | | +| `* | `expr * expr` | Arithmetic multiplication | `Mul` | +| `*=` | `var *= expr` | Arithmetic multiplication and assignment | `MulAssign` +| +| `*` | `*expr` | Dereference | `Deref` | +| `*` | `*const type`, `*mut type | Raw pointer | | +| `+ | `trait + trait`, `'a + trait` | Compound type constraint | | +| `+ | `expr + expr` | Arithmetic addition | `Add` | +| `+=` | `var += expr` | Arithmetic addition and assignment | `AddAssign` | +| `,` | `expr, expr` | Argument and element separator | | +| `- | `- expr` | Arithmetic negation | `Neg` | +| `- | `expr - expr` | Arithmetic subtraction | `Sub` | +| `-=` | `var -= expr` | Arithmetic subtraction and assignment | `SubAssign` | +| `-> | `fn(...) -> type`, `|…| -> type` | Function and closure return type | | +| `. | `expr.ident` | Member access | | +| `..` | `..`, `expr..`, `..expr`, `expr..expr` | Right-exclusive range literal +| `PartialOrd` | +| `..=` | `..=expr`, `expr..=expr` | Right-inclusive range literal | +`PartialOrd` | +| `..` | `..expr` | Struct literal update syntax | | +| `..` | `variant(x, ..)`, `struct_type { x, .. }` | “And the rest” pattern +binding | | +| `...` | `expr...expr` | (Deprecated, use `..=` instead) In a pattern: +inclusive range pattern | | +| `/ | `expr / expr` | Arithmetic division | `Div` | +| `/=` | `var /= expr` | Arithmetic division and assignment | `DivAssign` | +| `: | `pat: type`, `ident: type` | Constraints | | +| `:` | `ident: expr` | Struct field initializer | | +| `:` | `'a: loop {...}` | Loop label | | +| `; | `expr;` | Statement and item terminator | | +| `;` | `[...; len]` | Part of fixed-size array syntax | | +| `<<` | `expr << expr` | Left-shift | `Shl` | +| `<<=` | `var <<= expr` | Left-shift and assignment | `ShlAssign` | +| `<` | `expr < expr` | Less than comparison | `PartialOrd` | +| `<=` | `expr <= expr` | Less than or equal to comparison | `PartialOrd` | +| `=` | `var = expr`, `ident = type` | Assignment/equivalence | | +| `==` | `expr == expr` | Equality comparison | `PartialEq` | +| `=>` | `pat => expr` | Part of match arm syntax | | +| `>` | `expr > expr` | Greater than comparison | `PartialOrd` | +| `>=` | `expr >= expr` | Greater than or equal to comparison | `PartialOrd` | +| `>>` | `expr >> expr` | Right-shift | `Shr` | +| `>>=` | `var >>= expr` | Right-shift and assignment | `ShrAssign` | +| `@ | `ident @ pat` | Pattern binding | | +| `^` | `expr ^ expr` | Bitwise exclusive OR | `BitXor` | +| `^=` | `var ^= expr` | Bitwise exclusive OR and assignment | `BitXorAssign` | +| `| | `pat | pat` | Pattern alternatives | | +| `|` | `expr | expr` | Bitwise OR | `BitOr` | +| `|=` | `var |= expr` | Bitwise OR and assignment | `BitOrAssign` | +| `||` | `expr || expr` | Short-circuiting logical OR | | +| `? | `expr?` | Error propagation | | + +## Non-operator Symbols + +The following tables contain all symbols that don’t function as operators; that +is, they don’t behave like a function or method call. + +Table B-2 shows symbols that appear on their own and are valid in a variety of +locations. + +Table -2: Stand-Alone Syntax + +| Symbol | Explanation | +|---|---| +| `'ident | Named lifetime or loop label | +| `...u8`, `...i32`, `...f64`, `...usize`, and so on | Numeric literal of +specific type | +| `"..." | String literal | +| `r"..."`, `r#"..."#`, `r##"..."##`, and so on | Raw string literal; escape +characters not processed | +| `b"..."` | Byte string literal; constructs an array of bytes instead of a +string | +| `br"..."`, `br#"..."#`, `br##"..."##`, and so on | Raw byte string literal; +combination of raw and byte string literal | +| `'...' | Character literal | +| `b'...' | ASCII byte literal | +| `|…| expr | Closure | +| `! | Always-empty bottom type for diverging functions | +| `_ | “Ignored” pattern binding; also used to make integer literals readable | + +Table B-3 shows symbols that appear in the context of a path through the module +hierarchy to an item. + +Table -3: Path-Related Syntax + +| Symbol | Explanation | +|---|---| +| `ident::ident | Namespace path | +| `::path` | Path relative to the crate root (that is, an explicitly absolute +path) | +| `self::path` | Path relative to the current module (that is, an explicitly +relative path) | +| `super::path` | Path relative to the parent of the current module | +| `type::ident`, `::ident | Associated constants, functions, and +types | +| `::...` | Associated item for a type that cannot be directly named (for +example, `<&T>::...`, `<[T]>::...`, and so on) | +| `trait::method(...)` | Disambiguating a method call by naming the trait that +defines it | +| `type::method(...)` | Disambiguating a method call by naming the type for +which it’s defined | +| `::method(...)` | Disambiguating a method call by naming the +trait and type | + +Table B-4 shows symbols that appear in the context of using generic type +parameters. + +Table -4: Generics + +| Symbol | Explanation | +|---|---| +| `path<...>` | Specifies parameters to a generic type in a type (for example, +`Vec`) | +| `path::<...>, method::<...>` | Specifies parameters to a generic type, +function, or method in an expression; often referred to as turbofish (for +example, `"42".parse::()`) | +| `fn ident<...> ...` | Define generic function | +| `struct ident<...> ...` | Define generic structure | +| `enum ident<...> ...` | Define generic enumeration | +| `impl<...> ...` | Define generic implementation | +| `for<...> type` | Higher-ranked lifetime bounds | +| `type` | A generic type where one or more associated types have +specific assignments (for example, `Iterator`) | + +Table B-5 shows symbols that appear in the context of constraining generic type +parameters with trait bounds. + +Table -5: Trait Bound Constraints + +| Symbol | Explanation | +|---|---| +| T: U` | Generic parameter `T` constrained to types that implement `U` | +| `T: 'a` | Generic type `T` must outlive lifetime `'a` (meaning the type +cannot transitively contain any references with lifetimes shorter than `'a`) | +| `T: 'static` | Generic type `T` contains no borrowed references other than +`'static` ones | +| `'b: 'a` | Generic lifetime `'b` must outlive lifetime `'a` | +| `T: ?Sized` | Allow generic type parameter to be a dynamically sized type | +| `'a + trait`, `trait + trait` | Compound type constraint | + +Table B-6 shows symbols that appear in the context of calling or defining +macros and specifying attributes on an item. + +Table -6: Macros and Attributes + +| Symbol | Explanation | +|---|---| +| `#[meta]` | Outer attribute | +| `#![meta]` | Inner attribute | +| `$ident` | Macro substitution | +| `$ident:kind` | Macro capture | +| `$(…)…` | Macro repetition | +| `ident!(...)`, `ident!{...}`, `ident![...]` | Macro invocation | + +Table B-7 shows symbols that create comments. + +Table -7: Comments + +| Symbol | Explanation | +|---|---| +| `//` | Line comment | +| `//!` | Inner line doc comment | +| `///` | Outer line doc comment | +| `/*...*/` | Block comment | +| `/*!...*/` | Inner block doc comment | +| `/**...*/` | Outer block doc comment | + +Table B-8 shows symbols that appear in the context of using tuples. + +Table -8: Tuples + +| Symbol | Explanation | +|---|---| +| `() | Empty tuple (aka unit), both literal and type | +| `(expr)` | Parenthesized expression | +| `(expr,)` | Single-element tuple expression | +| `(type,)` | Single-element tuple type | +| `(expr, ...)` | Tuple expression | +| `(type, ...)` | Tuple type | +| `expr(expr, ...)` | Function call expression; also used to initialize tuple +`struct`s and tuple `enum` variants | +| `expr.0`, `expr.1`, and so on | Tuple indexing | + +Table B-9 shows the contexts in which curly brackets are used. + +Table -9: Curly Brackets + +| Context | Explanation | +|---|---| +| `{...}` | Block expression | +| `Type {...}` | `struct` literal | + +Table B-10 shows the contexts in which square brackets are used. + +Table -10: Square Brackets + +| Context | Explanation | +|---|---| +| `[...]` | Array literal | +| `[expr; len]` | Array literal containing `len` copies of `expr` | +| `[type; len]` | Array type containing `len` instances of `type` | +| `expr[expr]` | Collection indexing; overloadable (`Index`, `IndexMut`) | +| `expr[..]`, `expr[a..]`, `expr[..b]`, `expr[a..b]` | Collection indexing +pretending to be collection slicing, using `Range`, `RangeFrom`, `RangeTo`, or +`RangeFull` as the “index” | + + +Unmatched: diff --git a/nostarch/appendix_c.md b/nostarch/appendix_c.md new file mode 100644 index 00000000..53131eb5 --- /dev/null +++ b/nostarch/appendix_c.md @@ -0,0 +1,184 @@ + + +[TOC] + +## Appendix C: Derivable Traits + +In various places in the book, we’ve discussed the `derive` attribute, which +you can apply to a struct or enum definition. The `derive` attribute generates +code that will implement a trait with its own default implementation on the +type you’ve annotated with the `derive` syntax. + +In this appendix, we provide a reference of all the traits in the standard +library that you can use with `derive`. Each section covers: + +* What operators and methods deriving this trait will enable +* What the implementation of the trait provided by `derive` does +* What implementing the trait signifies about the type +* The conditions in which you’re allowed or not allowed to implement the trait +* Examples of operations that require the trait + +If you want different behavior from that provided by the `derive` attribute, +consult the standard library documentation for each trait for details on how to +manually implement them. + +The traits listed here are the only ones defined by the standard library that +can be implemented on your types using `derive`. Other traits defined in the +standard library don’t have sensible default behavior, so it’s up to you to +implement them in the way that makes sense for what you’re trying to accomplish. + +An example of a trait that can’t be derived is `Display`, which handles +formatting for end users. You should always consider the appropriate way to +display a type to an end user. What parts of the type should an end user be +allowed to see? What parts would they find relevant? What format of the data +would be most relevant to them? The Rust compiler doesn’t have this insight, so +it can’t provide appropriate default behavior for you. + +The list of derivable traits provided in this appendix is not comprehensive: +libraries can implement `derive` for their own traits, making the list of +traits you can use `derive` with truly open ended. Implementing `derive` +involves using a procedural macro, which is covered in “Macros” on page XX. + +## Debug for Programmer Output + +The `Debug` trait enables debug formatting in format strings, which you +indicate by adding `:?` within `{}` placeholders. + +The `Debug` trait allows you to print instances of a type for debugging +purposes, so you and other programmers using your type can inspect an instance +at a particular point in a program’s execution. + +The `Debug` trait is required, for example, in the use of the `assert_eq!` +macro. This macro prints the values of instances given as arguments if the +equality assertion fails so programmers can see why the two instances weren’t +equal. + +## PartialEq and Eq for Equality Comparisons + +The `PartialEq` trait allows you to compare instances of a type to check for +equality and enables use of the `==` and `!=` operators. + +Deriving `PartialEq` implements the `eq` method. When `PartialEq` is derived on +structs, two instances are equal only if *all* fields are equal, and the +instances are not equal if any fields are not equal. When derived on enums, +each variant is equal to itself and not equal to the other variants. + +The `PartialEq` trait is required, for example, with the use of the +`assert_eq!` macro, which needs to be able to compare two instances of a type +for equality. + +The `Eq` trait has no methods. Its purpose is to signal that for every value of +the annotated type, the value is equal to itself. The `Eq` trait can only be +applied to types that also implement `PartialEq`, although not all types that +implement `PartialEq` can implement `Eq`. One example of this is floating-point +number types: the implementation of floating-point numbers states that two +instances of the not-a-number (`NaN`) value are not equal to each other. + +An example of when `Eq` is required is for keys in a `HashMap` so that +the `HashMap` can tell whether two keys are the same. + +## PartialOrd and Ord for Ordering Comparisons + +The `PartialOrd` trait allows you to compare instances of a type for sorting +purposes. A type that implements `PartialOrd` can be used with the `<`, `>`, +`<=`, and `>=` operators. You can only apply the `PartialOrd` trait to types +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`. + +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 +definition. When derived on enums, variants of the enum declared earlier in the +enum definition are considered less than the variants listed later. + +The `PartialOrd` trait is required, for example, for the `gen_range` method +from the `rand` crate that generates a random value in the range specified by a +range expression. + +The `Ord` trait allows you to know that for any two values of the annotated +type, a valid ordering will exist. The `Ord` trait implements the `cmp` method, +which returns an `Ordering` rather than an `Option` because a valid +ordering will always be possible. You can only apply the `Ord` trait to types +that also implement `PartialOrd` and `Eq` (and `Eq` requires `PartialEq`). When +derived on structs and enums, `cmp` behaves the same way as the derived +implementation for `partial_cmp` does with `PartialOrd`. + +An example of when `Ord` is required is when storing values in a `BTreeSet`, +a data structure that stores data based on the sort order of the values. + +## Clone and Copy for Duplicating 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 “Variables and Data Interacting with Clone” on page XX 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 +fields or values in the type must also implement `Clone` to derive `Clone`. + +An example of when `Clone` is required is when calling the `to_vec` method on a +slice. The slice doesn’t own the type instances it contains, but the vector +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 “Stack-Only Data: Copy” on page +XX 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 +is being run. That way, all programmers can assume that copying a value will be +very fast. + +You can derive `Copy` on any type whose parts all implement `Copy`. A type that +implements `Copy` must also implement `Clone` because a type that implements +`Copy` has a trivial implementation of `Clone` that performs the same task as +`Copy`. + +The `Copy` trait is rarely required; types that implement `Copy` have +optimizations available, meaning you don’t have to call `clone`, which makes +the code more concise. + +Everything possible with `Copy` you can also accomplish with `Clone`, but the +code might be slower or have to use `clone` in places. + +## Hash for Mapping a Value to a Value of Fixed Size + +The `Hash` trait allows you to take an instance of a type of arbitrary size and +map that instance to a value of fixed size using a hash function. Deriving +`Hash` implements the `hash` method. The derived implementation of the `hash` +method combines the result of calling `hash` on each of the parts of the type, +meaning all fields or values must also implement `Hash` to derive `Hash`. + +An example of when `Hash` is required is in storing keys in a `HashMap` +to store data efficiently. + +## Default for Default Values + +The `Default` trait allows you to create a default value for a type. Deriving +`Default` implements the `default` function. The derived implementation of the +`default` function calls the `default` function on each part of the type, +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 “Creating Instances from Other Instances with Struct +Update Syntax” on page XX. 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 +`Option` instances, for example. If the `Option` is `None`, the method +`unwrap_or_default` will return the result of `Default::default` for the type +`T` stored in the `Option`. + diff --git a/nostarch/appendix_d.md b/nostarch/appendix_d.md new file mode 100644 index 00000000..96b73e95 --- /dev/null +++ b/nostarch/appendix_d.md @@ -0,0 +1,175 @@ + + +[TOC] + +## Appendix D: Useful Development Tools + +In this appendix, we talk about some useful development tools that the Rust +project provides. We’ll look at automatic formatting, quick ways to apply +warning fixes, a linter, and integrating with IDEs. + +## Automatic Formatting with rustfmt + +The `rustfmt` tool reformats your code according to the community code style. +Many collaborative projects use `rustfmt` to prevent arguments about which +style to use when writing Rust: everyone formats their code using the tool. + +Rust installations include `rustfmt` by default, so you should already have the +programs `rustfmt` and `cargo-fmt` on your system. These two commands are +analagous to `rustc` and `cargo` in that `rustfmt` allows finer-grained control +and `cargo-fmt` understands conventions of a project that uses Cargo. To format +any Cargo project, enter the following: + +``` +$ cargo fmt +``` + +Running this command reformats all the Rust code in the current crate. This +should only change the code style, not the code semantics. For more information +on `rustfmt`, see its documentation at *https://github.com/rust-lang/rustfmt*. + +## Fix Your Code with rustfix + +The `rustfix` tool is included with Rust installations and can automatically +fix compiler warnings that have a clear way to correct the problem that’s +likely what you want. You’ve probably seen compiler warnings before. For +example, consider this code: + +Filename: src/main.rs + +``` +fn do_something() {} + +fn main() { + for i in 0..100 { + do_something(); + } +} +``` + +Here, we’re calling the `do_something` function 100 times, but we never use the +variable `i` in the body of the `for` loop. Rust warns us about that: + +``` +$ cargo build + Compiling myprogram v0.1.0 (file:///projects/myprogram) +warning: unused variable: `i` + --> src/main.rs:4:9 + | +4 | for i in 0..100 { + | ^ help: consider using `_i` instead + | + = note: #[warn(unused_variables)] on by default + + Finished dev [unoptimized + debuginfo] target(s) in 0.50s +``` + +The warning suggests that we use `_i` as a name instead: the underscore +indicates that we intend for this variable to be unused. We can automatically +apply that suggestion using the `rustfix` tool by running the command `cargo +fix`: + +``` +$ cargo fix + Checking myprogram v0.1.0 (file:///projects/myprogram) + Fixing src/main.rs (1 fix) + Finished dev [unoptimized + debuginfo] target(s) in 0.59s +``` + +When we look at *src/main.rs* again, we’ll see that `cargo fix` has changed the +code: + +Filename: src/main.rs + +``` +fn do_something() {} + +fn main() { + for _i in 0..100 { + do_something(); + } +} +``` + +The `for` loop variable is now named `_i`, and the warning no longer appears. + +You can also use the `cargo fix` command to transition your code between +different Rust editions. Editions are covered in Appendix E. + +## More Lints with Clippy + +The Clippy tool is a collection of lints to analyze your code so you can catch +common mistakes and improve your Rust code. Clippy is included with standard +Rust installations. + +To run Clippy’s lints on any Cargo project, enter the following: + +``` +$ cargo clippy +``` + +For example, say you write a program that uses an approximation of a +mathematical constant, such as pi, as this program does: + +Filename: src/main.rs + +``` +fn main() { + let x = 3.1415; + let r = 8.0; + println!("the area of the circle is {}", x * r * r); +} +``` + +Running `cargo clippy` on this project results in this error: + +``` +error: approximate value of `f{32, 64}::consts::PI` found + --> src/main.rs:2:13 + | +2 | let x = 3.1415; + | ^^^^^^ + | + = note: `#[deny(clippy::approx_constant)]` on by default + = help: consider using the constant directly + = help: for further information visit https://rust-lang.github.io/rust- +clippy/master/index.html#approx_constant +``` + +This error lets you know that Rust already has a more precise `PI` constant +defined, and that your program would be more correct if you used the constant +instead. You would then change your code to use the `PI` constant. + +The following code doesn’t result in any errors or warnings from Clippy: + +Filename: src/main.rs + +``` +fn main() { + let x = std::f64::consts::PI; + let r = 8.0; + println!("the area of the circle is {}", x * r * r); +} +``` + +For more information on Clippy, see its documentation at +*https://github.com/rust-lang/rust-clippy**.* + +## IDE Integration Using rust-analyzer + +To help with IDE integration, the Rust community recommends using +`rust-analyzer`. This tool is a set of compiler-centric utilities that speak +Language Server Protocol, which is a specification for IDEs and programming +languages to communicate with each other. Different clients can use +`rust-analyzer`, such as the Rust analyzer plug-in for Visual Studio Code at +*https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer*. + +Visit the `rust-analyzer` project’s home page at +*https://rust-analyzer.github.io* for installation instructions, then install +the language server support in your particular IDE. Your IDE will gain +capabilities such as autocompletion, jump to definition, and inline errors + diff --git a/nostarch/appendix_e.md b/nostarch/appendix_e.md new file mode 100644 index 00000000..ddb12b78 --- /dev/null +++ b/nostarch/appendix_e.md @@ -0,0 +1,66 @@ + + +[TOC] + +## Appendix E: Editions + +In Chapter 1, you saw that `cargo new` adds a bit of metadata to your +*Cargo.toml* file about an edition. This appendix talks about what that means! + +The Rust language and compiler have a six-week release cycle, meaning users get +a constant stream of new features. Other programming languages release larger +changes less often; Rust releases smaller updates more frequently. After a +while, all of these tiny changes add up. But from release to release, it can be +difficult to look back and say, “Wow, between Rust 1.10 and Rust 1.31, Rust has +changed a lot!” + +Every two or three years, the Rust team produces a new Rust *edition*. Each +edition brings together the features that have landed into a clear package with +fully updated documentation and tooling. New editions ship as part of the usual +six-week release process. + +Editions serve different purposes for different people: + +* For active Rust users, a new edition brings together incremental changes into +an easy-to-understand package. +* For non-users, a new edition signals that some major advancements have +landed, which might make Rust worth another look. +* For those developing Rust, a new edition provides a rallying point for the +project as a whole. + +At the time of this writing, three Rust editions are available: Rust 2015, Rust +2018, and Rust 2021. This book is written using Rust 2021 edition idioms. + +The `edition` key in *Cargo.toml* indicates which edition the compiler should +use for your code. If the key doesn’t exist, Rust uses `2015` as the edition +value for backward compatibility reasons. + +Each project can opt in to an edition other than the default 2015 edition. +Editions can contain incompatible changes, such as including a new keyword that +conflicts with identifiers in code. However, unless you opt in to those +changes, your code will continue to compile even as you upgrade the Rust +compiler version you use. + +All Rust compiler versions support any edition that existed prior to that +compiler’s release, and they can link crates of any supported editions +together. Edition changes only affect the way the compiler initially parses +code. Therefore, if you’re using Rust 2015 and one of your dependencies uses +Rust 2018, your project will compile and be able to use that dependency. The +opposite situation, where your project uses Rust 2018 and a dependency uses +Rust 2015, works as well. + +To be clear: most features will be available on all editions. Developers using +any Rust edition will continue to see improvements as new stable releases are +made. However, in some cases, mainly when new keywords are added, some new +features might only be available in later editions. You will need to switch +editions if you want to take advantage of such features. + +For more details, *The* *Edition Guide* at +*https://doc.rust-lang.org/stable/edition-guide* is a complete book about +editions that enumerates the differences between editions and explains how to +automatically upgrade your code to a new edition via `cargo fix`. + diff --git a/nostarch/chapter01.md b/nostarch/chapter01.md index e44c821c..3379b14f 100644 --- a/nostarch/chapter01.md +++ b/nostarch/chapter01.md @@ -33,7 +33,6 @@ differ slightly between versions because Rust often improves error messages and warnings. In other words, any newer, stable version of Rust you install using these steps should work as expected with the content of this book. - > ### Command Line Notation > > In this chapter and throughout the book, we’ll show some commands used in the @@ -222,7 +221,7 @@ fn main() { } ``` -A program that prints `Hello, world!` +Listing 1-1: A program that prints `Hello, world!` Save the file and go back to your terminal window in the *~/projects/hello_world* directory. On Linux or macOS, enter the following @@ -271,8 +270,8 @@ line as the function declaration, adding one space in between. > Note: If you want to stick to a standard style across Rust projects, you can use an automatic formatter tool called `rustfmt` to format your code in a -particular style (more on rustfmt in Appendix D). The Rust team has included -this tool with the standard Rust distribution, as rustc is, so it should +particular style (more on `rustfmt` in Appendix D). The Rust team has included +this tool with the standard Rust distribution, as `rustc` is, so it should already be installed on your computer! The body of the `main` function holds the following code: @@ -430,7 +429,7 @@ edition = "2021" [dependencies] ``` -Contents of *Cargo.toml* generated by `cargo new` +Listing 1-2: Contents of *Cargo.toml* generated by `cargo new` This file is in the *TOML* (*Tom’s Obvious, Minimal Language*) format, which is Cargo’s configuration format. diff --git a/nostarch/chapter02.md b/nostarch/chapter02.md index 8ab91c2e..f9876a6a 100644 --- a/nostarch/chapter02.md +++ b/nostarch/chapter02.md @@ -106,10 +106,7 @@ fn main() { } ``` -Code that gets a guess from the user and prints it - -Prod: Please renumber the listing captions for this chapter—Listing 2-1, -Listing 2-2, etc. +Listing 2-1: Code that gets a guess from the user and prints it This code contains a lot of information, so let’s go over it line by line. To obtain user input and then print the result as output, we need to bring the @@ -417,8 +414,8 @@ $ cargo build Finished dev [unoptimized + debuginfo] target(s) in 2.53s ``` -The output from running `cargo build` after adding the `rand` crate as a -dependency +Listing 2-2: The output from running `cargo build` after adding the `rand` +crate as a dependency You may see different version numbers (but they will all be compatible with the code, thanks to SemVer!) and different lines (depending on the operating @@ -541,7 +538,7 @@ fn main() { } ``` -Adding code to generate a random number +Listing 2-3: Adding code to generate a random number First we add the line `use rand::Rng;` [1]. The `Rng` trait defines methods that random number generators implement, and this trait must be in scope for us @@ -623,7 +620,7 @@ fn main() { } ``` -Handling the possible return values of comparing two numbers +Listing 2-4: Handling the possible return values of comparing two numbers First we add another `use` statement [1], bringing a type called `std::cmp::Ordering` into scope from the standard library. The `Ordering` type @@ -748,8 +745,6 @@ with `secret_number` means Rust will infer that `secret_number` should be a `u32` as well. So now the comparison will be between two values of the same type! -Comp: Note that there is an emoji in the below paragraph - The `parse` method will only work on characters that can logically be converted into numbers and so can easily cause errors. If, for example, the string contained `A`👍`%`, there would be no way to convert that to a number. Because @@ -900,8 +895,8 @@ println!("You guessed: {guess}"); --snip-- ``` -Ignoring a non-number guess and asking for another guess instead of crashing -the program +Listing 2-5: Ignoring a non-number guess and asking for another guess instead +of crashing the program We switch from an `expect` call to a `match` expression to move from crashing on an error to handling the error. Remember that `parse` returns a `Result` @@ -995,7 +990,7 @@ fn main() { } ``` -Complete guessing game code +Listing 2-6: Complete guessing game code At this point, you’ve successfully built the guessing game. Congratulations! diff --git a/nostarch/chapter03.md b/nostarch/chapter03.md index b7a54aee..249032fd 100644 --- a/nostarch/chapter03.md +++ b/nostarch/chapter03.md @@ -18,7 +18,6 @@ Specifically, you’ll learn about variables, basic types, functions, comments, and control flow. These foundations will be in every Rust program, and learning them early will give you a strong core to start from. - > ### Keywords > > The Rust language has a set of *keywords* that are reserved for use by the @@ -313,7 +312,7 @@ start with `i` instead of `u`) that takes up 32 bits of space. Table 3-1 shows the built-in integer types in Rust. We can use any of these variants to declare the type of an integer value. -Integer Types in Rust +Table 3-1: Integer Types in Rust | Length | Signed | Unsigned | |---|---|---| @@ -350,7 +349,7 @@ such as `57u8`, to designate the type. Number literals can also use `_` as a visual separator to make the number easier to read, such as `1_000`, which will have the same value as if you had specified `1000`. -Integer Literals in Rust +Table 3-2: Integer Literals in Rust | Number literals | Example | |---|---| @@ -365,7 +364,6 @@ defaults are generally good places to start: integer types default to `i32`. The primary situation in which you’d use `isize` or `usize` is when indexing some sort of collection. - > ### Integer Overflow > > Let’s say you have a variable of type `u8` that can hold values between 0 and @@ -852,9 +850,10 @@ understand. Other languages don’t have the same distinctions, so let’s look what statements and expressions are and how their differences affect the bodies of functions. -* Statements - are instructions that perform some action and do not return a -value. -* Expressions - evaluate to a resultant value. Let’s look at some examples. +* **Statements **: are instructions that perform some action and do not return +a value. +* **Expressions **: evaluate to a resultant value. Let’s look at some examples. + We’ve actually already used statements and expressions. Creating a variable and assigning a value to it with the `let` keyword is a statement. In Listing 3-1, `let y = 6;` is a statement. @@ -867,7 +866,7 @@ fn main() { } ``` -A `main` function declaration containing one statement +Listing 3-1: A `main` function declaration containing one statement Function definitions are also statements; the entire preceding example is a statement in itself. @@ -1280,7 +1279,7 @@ fn main() { } ``` -Assigning the result of an `if` expression to a variable +Listing 3-2: Assigning the result of an `if` expression to a variable The `number` variable will be bound to a value based on the outcome of the `if` expression. Run this code to see what happens: @@ -1509,7 +1508,8 @@ fn main() { } ``` -Using a `while` loop to run code while a condition evaluates to `true` +Listing 3-3: Using a `while` loop to run code while a condition evaluates to +`true` This construct eliminates a lot of nesting that would be necessary if you used `loop`, `if`, `else`, and `break`, and it’s clearer. While a condition @@ -1536,7 +1536,7 @@ fn main() { } ``` -Looping through each element of a collection using a `while` loop +Listing 3-4: Looping through each element of a collection using a `while` loop Here, the code counts up through the elements in the array. It starts at index `0`, and then loops until it reaches the final index in the array (that is, @@ -1581,7 +1581,7 @@ fn main() { } ``` -Looping through each element of a collection using a `for` loop +Listing 3-5: Looping through each element of a collection using a `for` loop When we run this code, we’ll see the same output as in Listing 3-4. More importantly, we’ve now increased the safety of the code and eliminated the diff --git a/nostarch/chapter05.md b/nostarch/chapter05.md index 3946227f..9881a3b7 100644 --- a/nostarch/chapter05.md +++ b/nostarch/chapter05.md @@ -47,7 +47,7 @@ struct User { } ``` -A `User` struct definition +Listing 5-1: A `User` struct definition To use a struct after we’ve defined it, we create an *instance* of that struct by specifying concrete values for each of the fields. We create an instance by @@ -72,7 +72,7 @@ fn main() { } ``` -Creating an instance of the `User` struct +Listing 5-2: Creating an instance of the `User` struct To get a specific value from a struct, we use dot notation. For example, to access this user’s email address, we use `user1.email`. If the instance is @@ -95,7 +95,7 @@ fn main() { } ``` -Changing the value in the `email` field of a `User` instance +Listing 5-3: Changing the value in the `email` field of a `User` instance Note that the entire instance must be mutable; Rust doesn’t allow us to mark only certain fields as mutable. As with any expression, we can construct a new @@ -117,8 +117,8 @@ fn build_user(email: String, username: String) -> User { } ``` -A `build_user` function that takes an email and username and returns a `User` -instance +Listing 5-4: A `build_user` function that takes an email and username and +returns a `User` instance It makes sense to name the function parameters with the same name as the struct fields, but having to repeat the `email` and `username` field names and @@ -143,8 +143,8 @@ fn build_user(email: String, username: String) -> User { } ``` -A `build_user` function that uses field init shorthand because the `username` -and `email` parameters have the same name as struct fields +Listing 5-5: A `build_user` function that uses field init shorthand because the +`username` and `email` parameters have the same name as struct fields Here, we’re creating a new instance of the `User` struct, which has a field named `email`. We want to set the `email` field’s value to the value in the @@ -177,7 +177,7 @@ fn main() { } ``` -Creating a new `User` instance using one of the values from `user1` +Listing 5-6: Creating a new `User` instance using one of the values from `user1` Using struct update syntax, we can achieve the same effect with less code, as shown in Listing 5-7. The syntax `..` specifies that the remaining fields not @@ -197,8 +197,8 @@ fn main() { } ``` -Using struct update syntax to set a new `email` value for a `User` instance but -to use the rest of the values from `user1` +Listing 5-7: Using struct update syntax to set a new `email` value for a `User` +instance but to use the rest of the values from `user1` The code in Listing 5-7 also creates an instance in `user2` that has a different value for `email` but has the same values for the `username`, @@ -282,7 +282,6 @@ have a known result for testing purposes. We wouldn’t need any data to implement that behavior! You’ll see in Chapter 10 how to define traits and implement them on any type, including unit-like structs. - > ### Ownership of Struct Data > > In the `User` struct definition in Listing 5-1, we used the owned `String` @@ -381,8 +380,8 @@ fn area(width: u32, height: u32) -> u32 { } ``` -Calculating the area of a rectangle specified by separate width and height -variables +Listing 5-8: Calculating the area of a rectangle specified by separate width +and height variables Now, run this program using `cargo run`: @@ -427,7 +426,7 @@ fn area(dimensions: (u32, u32)) -> u32 { } ``` -Specifying the width and height of the rectangle with a tuple +Listing 5-9: Specifying the width and height of the rectangle with a tuple In one way, this program is better. Tuples let us add a bit of structure, and we’re now passing just one argument [1]. But in another way, this version is @@ -472,7 +471,7 @@ fn main() { } ``` -Defining a `Rectangle` struct +Listing 5-10: Defining a `Rectangle` struct Here, we’ve defined a struct and named it `Rectangle` [1]. Inside the curly brackets, we defined the fields as `width` and `height`, both of which have @@ -520,7 +519,7 @@ fn main() { } ``` -Attempting to print a `Rectangle` instance +Listing 5-11: Attempting to print a `Rectangle` instance When we compile this code, we get an error with this core message: @@ -590,8 +589,8 @@ fn main() { } ``` -Adding the attribute to derive the `Debug` trait and printing the `Rectangle` -instance using debug formatting +Listing 5-12: Adding the attribute to derive the `Debug` trait and printing the +`Rectangle` instance using debug formatting Now when we run the program, we won’t get any errors, and we’ll see the following output: @@ -728,7 +727,7 @@ fn main() { } ``` -Defining an `area` method on the `Rectangle` struct +Listing 5-13: Defining an `area` method on the `Rectangle` struct To define the function within the context of `Rectangle`, we start an `impl` (implementation) block for `Rectangle` [1]. Everything within this `impl` block @@ -811,7 +810,6 @@ to that field as part of the type’s public API. We will discuss what public an private are and how to designate a field or method as public or private in Chapter 7. - > ### Where’s the -> Operator? > > In C and C++, two different operators are used for calling methods: you use @@ -871,7 +869,7 @@ fn main() { } ``` -Using the as-yet-unwritten `can_hold` method +Listing 5-14: Using the as-yet-unwritten `can_hold` method The expected output would look like the following because both dimensions of `rect2` are smaller than the dimensions of `rect1`, but `rect3` is wider than @@ -910,8 +908,8 @@ impl Rectangle { } ``` -Implementing the `can_hold` method on `Rectangle` that takes another -`Rectangle` instance as a parameter +Listing 5-15: Implementing the `can_hold` method on `Rectangle` that takes +another `Rectangle` instance as a parameter When we run this code with the `main` function in Listing 5-14, we’ll get our desired output. Methods can take multiple parameters that we add to the @@ -977,7 +975,7 @@ impl Rectangle { } ``` -Rewriting Listing 5-15 using multiple `impl` blocks +Listing 5-16: Rewriting Listing 5-15 using multiple `impl` blocks There’s no reason to separate these methods into multiple `impl` blocks here, but this is valid syntax. We’ll see a case in which multiple `impl` blocks are diff --git a/nostarch/chapter06.md b/nostarch/chapter06.md index f75403d7..ea4328be 100644 --- a/nostarch/chapter06.md +++ b/nostarch/chapter06.md @@ -106,7 +106,8 @@ Listing 6-1. }; ``` -Storing the data and `IpAddrKind` variant of an IP address using a `struct` +Listing 6-1: Storing the data and `IpAddrKind` variant of an IP address using a +`struct` Here, we’ve defined a struct `IpAddr` [2] that has two fields: a `kind` field [3] that is of type `IpAddrKind` (the enum we defined previously [1]) and an @@ -204,7 +205,8 @@ enum Message { } ``` -A `Message` enum whose variants each store different amounts and types of values +Listing 6-2: A `Message` enum whose variants each store different amounts and +types of values This enum has four variants with different types: @@ -439,8 +441,8 @@ fn value_in_cents(coin: Coin) -> u8 { } ``` -An enum and a `match` expression that has the variants of the enum as its -patterns +Listing 6-3: An enum and a `match` expression that has the variants of the enum +as its patterns Let’s break down the `match` in the `value_in_cents` function. First we list the `match` keyword followed by an expression, which in this case is the value @@ -514,7 +516,8 @@ enum Coin { } ``` -A `Coin` enum in which the `Quarter` variant also holds a `UsState` value +Listing 6-4: A `Coin` enum in which the `Quarter` variant also holds a +`UsState` value Let’s imagine that a friend is trying to collect all 50 state quarters. While we sort our loose change by coin type, we’ll also call out the name of the @@ -576,7 +579,7 @@ let six = plus_one(five); 3 let none = plus_one(None); 4 ``` -A function that uses a `match` expression on an `Option` +Listing 6-5: A function that uses a `match` expression on an `Option` Let’s examine the first execution of `plus_one` in more detail. When we call `plus_one(five)` [3], the variable `x` in the body of `plus_one` will have the @@ -757,7 +760,8 @@ match config_max { } ``` -A `match` that only cares about executing code when the value is `Some` +Listing 6-6: A `match` that only cares about executing code when the value is +`Some` If the value is `Some`, we print out the value in the `Some` variant by binding the value to the variable `max` in the pattern. We don’t want to do anything diff --git a/nostarch/chapter07.md b/nostarch/chapter07.md index b4a9c4e2..8b2dce77 100644 --- a/nostarch/chapter07.md +++ b/nostarch/chapter07.md @@ -263,7 +263,8 @@ mod front_of_house { } ``` -A `front_of_house` module containing other modules that then contain functions +Listing 7-1: A `front_of_house` module containing other modules that then +contain functions We define a module with the `mod` keyword followed by the name of the module (in this case, `front_of_house`). The body of the module then goes inside curly @@ -297,7 +298,7 @@ crate └── take_payment ``` -The module tree for the code in Listing 7-1 +Listing 7-2: The module tree for the code in Listing 7-1 This tree shows how some of the modules nest inside other modules; for example, `hosting` nests inside `front_of_house`. The tree also shows that some modules @@ -360,7 +361,8 @@ pub fn eat_at_restaurant() { } ``` -Calling the `add_to_waitlist` function using absolute and relative paths +Listing 7-3: Calling the `add_to_waitlist` function using absolute and relative +paths The first time we call the `add_to_waitlist` function in `eat_at_restaurant`, we use an absolute path. The `add_to_waitlist` function is defined in the same @@ -422,7 +424,7 @@ note: the module `hosting` is defined here | ^^^^^^^^^^^ ``` -Compiler errors from building the code in Listing 7-3 +Listing 7-4: Compiler errors from building the code in Listing 7-3 The error messages say that module `hosting` is private. In other words, we have the correct paths for the `hosting` module and the `add_to_waitlist` @@ -464,7 +466,8 @@ mod front_of_house { --snip-- ``` -Declaring the `hosting` module as `pub` to use it from `eat_at_restaurant` +Listing 7-5: Declaring the `hosting` module as `pub` to use it from +`eat_at_restaurant` Unfortunately, the code in Listing 7-5 still results in compiler errors, as shown in Listing 7-6. @@ -497,7 +500,7 @@ note: the function `add_to_waitlist` is defined here | ^^^^^^^^^^^^^^^^^^^^ ``` -Compiler errors from building the code in Listing 7-5 +Listing 7-6: Compiler errors from building the code in Listing 7-5 What happened? Adding the `pub` keyword in front of `mod hosting` makes the module public. With this change, if we can access `front_of_house`, we can @@ -527,8 +530,8 @@ mod front_of_house { --snip-- ``` -Adding the `pub` keyword to `mod hosting` and `fn add_to_waitlist` lets us call -the function from `eat_at_restaurant`. +Listing 7-7: Adding the `pub` keyword to `mod hosting` and `fn add_to_waitlist` +lets us call the function from `eat_at_restaurant`. Now the code will compile! To see why adding the `pub` keyword lets us use these paths in `add_to_waitlist` with respect to the privacy rules, let’s look @@ -611,7 +614,7 @@ mod back_of_house { } ``` -Calling a function using a relative path starting with `super` +Listing 7-8: Calling a function using a relative path starting with `super` The `fix_incorrect_order` function is in the `back_of_house` module, so we can use `super` to go to the parent module of `back_of_house`, which in this case @@ -668,7 +671,7 @@ pub fn eat_at_restaurant() { } ``` -A struct with some public fields and some private fields +Listing 7-9: A struct with some public fields and some private fields Because the `toast` field in the `back_of_house::Breakfast` struct is public, in `eat_at_restaurant` we can write and read to the `toast` field using dot @@ -702,7 +705,7 @@ pub fn eat_at_restaurant() { } ``` -Designating an enum as public makes all its variants public. +Listing 7-10: Designating an enum as public makes all its variants public. Because we made the `Appetizer` enum public, we can use the `Soup` and `Salad` variants in `eat_at_restaurant`. @@ -747,7 +750,7 @@ pub fn eat_at_restaurant() { } ``` -Bringing a module into scope with `use` +Listing 7-11: Bringing a module into scope with `use` Adding `use` and a path in a scope is similar to creating a symbolic link in the filesystem. By adding `use crate::front_of_house::hosting` in the crate @@ -778,7 +781,7 @@ mod customer { } ``` -A `use` statement only applies in the scope it’s in. +Listing 7-12: A `use` statement only applies in the scope it’s in. The compiler error shows that the shortcut no longer applies within the `customer` module: @@ -827,8 +830,8 @@ pub fn eat_at_restaurant() { } ``` -Bringing the `add_to_waitlist` function into scope with `use`, which is -unidiomatic +Listing 7-13: Bringing the `add_to_waitlist` function into scope with `use`, +which is unidiomatic Although both Listing 7-11 and Listing 7-13 accomplish the same task, Listing 7-11 is the idiomatic way to bring a function into scope with `use`. Bringing @@ -854,7 +857,7 @@ fn main() { } ``` -Bringing `HashMap` into scope in an idiomatic way +Listing 7-14: Bringing `HashMap` into scope in an idiomatic way There’s no strong reason behind this idiom: it’s just the convention that has emerged, and folks have gotten used to reading and writing Rust code this way. @@ -879,8 +882,8 @@ fn function2() -> io::Result<()> { } ``` -Bringing two types with the same name into the same scope requires using their -parent modules. +Listing 7-15: Bringing two types with the same name into the same scope +requires using their parent modules. As you can see, using the parent modules distinguishes the two `Result` types. If instead we specified `use std::fmt::Result` and `use std::io::Result`, we’d @@ -909,7 +912,7 @@ fn function2() -> IoResult<()> { } ``` -Renaming a type when it’s brought into scope with the `as` keyword +Listing 7-16: Renaming a type when it’s brought into scope with the `as` keyword In the second `use` statement, we chose the new name `IoResult` for the `std::io::Result` type, which won’t conflict with the `Result` from `std::fmt` @@ -944,7 +947,8 @@ pub fn eat_at_restaurant() { } ``` -Making a name available for any code to use from a new scope with `pub use` +Listing 7-17: Making a name available for any code to use from a new scope with +`pub use` Before this change, external code would have to call the `add_to_waitlist` function by using the path @@ -1040,7 +1044,8 @@ use std::{cmp::Ordering, io}; --snip-- ``` -Specifying a nested path to bring multiple items with the same prefix into scope +Listing 7-18: Specifying a nested path to bring multiple items with the same +prefix into scope In bigger programs, bringing many items into scope from the same crate or module using nested paths can reduce the number of separate `use` statements @@ -1058,7 +1063,7 @@ use std::io; use std::io::Write; ``` -Two `use` statements where one is a subpath of the other +Listing 7-19: Two `use` statements where one is a subpath of the other The common part of these two paths is `std::io`, and that’s the complete first path. To merge these two paths into one `use` statement, we can use `self` in @@ -1070,7 +1075,7 @@ Filename: src/lib.rs use std::io::{self, Write}; ``` -Combining the paths in Listing 7-19 into one `use` statement +Listing 7-20: Combining the paths in Listing 7-19 into one `use` statement This line brings `std::io` and `std::io::Write` into scope. @@ -1123,7 +1128,7 @@ pub fn eat_at_restaurant() { } ``` -Declaring the `front_of_house` module whose body will be in +Listing 7-21: Declaring the `front_of_house` module whose body will be in *src/front_of_house.rs* Next, place the code that was in the curly brackets into a new file named @@ -1139,7 +1144,8 @@ pub mod hosting { } ``` -Definitions inside the `front_of_house` module in *src/front_of_house.rs* +Listing 7-22: Definitions inside the `front_of_house` module in +*src/front_of_house.rs* Note that you only need to load a file using a `mod` declaration *once* in your module tree. Once the compiler knows the file is part of the project (and knows diff --git a/nostarch/chapter08.md b/nostarch/chapter08.md index 8abb9c80..39a325fb 100644 --- a/nostarch/chapter08.md +++ b/nostarch/chapter08.md @@ -47,7 +47,7 @@ Listing 8-1. let v: Vec = Vec::new(); ``` -Creating a new, empty vector to hold values of type `i32` +Listing 8-1: Creating a new, empty vector to hold values of type `i32` Note that we added a type annotation here. Because we aren’t inserting any values into this vector, Rust doesn’t know what kind of elements we intend to @@ -70,7 +70,7 @@ page XX. let v = vec![1, 2, 3]; ``` -Creating a new vector containing values +Listing 8-2: Creating a new vector containing values Because we’ve given initial `i32` values, Rust can infer that the type of `v` is `Vec`, and the type annotation isn’t necessary. Next, we’ll look at how @@ -90,7 +90,7 @@ v.push(7); v.push(8); ``` -Using the `push` method to add values to a vector +Listing 8-3: Using the `push` method to add values to a vector As with any variable, if we want to be able to change its value, we need to make it mutable using the `mut` keyword, as discussed in Chapter 3. The numbers @@ -119,7 +119,8 @@ match third { } ``` -Using indexing syntax and using the `get` method to access an item in a vector +Listing 8-4: Using indexing syntax and using the `get` method to access an item +in a vector Note a few details here. We use the index value of `2` to get the third element [1] because vectors are indexed by number, starting at zero. Using `&` and `[]` @@ -140,8 +141,8 @@ let does_not_exist = &v[100]; let does_not_exist = v.get(100); ``` -Attempting to access the element at index 100 in a vector containing five -elements +Listing 8-5: Attempting to access the element at index 100 in a vector +containing five elements When we run this code, the first `[]` method will cause the program to panic because it references a nonexistent element. This method is best used when you @@ -178,7 +179,8 @@ v.push(6); println!("The first element is: {first}"); ``` -Attempting to add an element to a vector while holding a reference to an item +Listing 8-6: Attempting to add an element to a vector while holding a reference +to an item Compiling this code will result in this error: @@ -224,8 +226,8 @@ for i in &v { } ``` -Printing each element in a vector by iterating over the elements using a `for` -loop +Listing 8-7: Printing each element in a vector by iterating over the elements +using a `for` loop We can also iterate over mutable references to each element in a mutable vector in order to make changes to all the elements. The `for` loop in Listing 8-8 @@ -238,7 +240,7 @@ for i in &mut v { } ``` -Iterating over mutable references to elements in a vector +Listing 8-8: Iterating over mutable references to elements in a vector 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 `+=` @@ -281,7 +283,7 @@ let row = vec![ ]; ``` -Defining an `enum` to store values of different types in one vector +Listing 8-9: Defining an `enum` to store values of different types in one vector Rust needs to know what types will be in the vector at compile time so it knows exactly how much memory on the heap will be needed to store each element. We @@ -313,7 +315,7 @@ annotated in Listing 8-10. } // <- v goes out of scope and is freed here ``` -Showing where the vector and its elements are dropped +Listing 8-10: Showing where the vector and its elements are dropped When the vector gets dropped, all of its contents are also dropped, meaning the integers it holds will be cleaned up. The borrow checker ensures that any @@ -369,7 +371,7 @@ function to create an instance, shown in Listing 8-11. let mut s = String::new(); ``` -Creating a new, empty `String` +Listing 8-11: Creating a new, empty `String` This line creates a new, empty string called `s`, into which we can then load data. Often, we’ll have some initial data with which we want to start the @@ -386,7 +388,8 @@ let s = data.to_string(); let s = "initial contents".to_string(); ``` -Using the `to_string` method to create a `String` from a string literal +Listing 8-12: Using the `to_string` method to create a `String` from a string +literal This code creates a string containing `initial contents`. @@ -398,7 +401,8 @@ that uses `to_string`. let s = String::from("initial contents"); ``` -Using the `String::from` function to create a `String` from a string literal +Listing 8-13: Using the `String::from` function to create a `String` from a +string literal Because strings are used for so many things, we can use many different generic APIs for strings, providing us with a lot of options. Some of them can seem @@ -423,7 +427,7 @@ let hello = String::from("Здравствуйте"); let hello = String::from("Hola"); ``` -Storing greetings in different languages in strings +Listing 8-14: Storing greetings in different languages in strings All of these are valid `String` values. @@ -443,7 +447,7 @@ let mut s = String::from("foo"); s.push_str("bar"); ``` -Appending a string slice to a `String` using the `push_str` method +Listing 8-15: Appending a string slice to a `String` using the `push_str` method After these two lines, `s` will contain `foobar`. The `push_str` method takes a string slice because we don’t necessarily want to take ownership of the @@ -457,7 +461,7 @@ s1.push_str(s2); println!("s2 is {s2}"); ``` -Using a string slice after appending its contents to a `String` +Listing 8-16: Using a string slice after appending its contents to a `String` If the `push_str` method took ownership of `s2`, we wouldn’t be able to print its value on the last line. However, this code works as we’d expect! @@ -471,7 +475,7 @@ let mut s = String::from("lo"); s.push('l'); ``` -Adding one character to a `String` value using `push` +Listing 8-17: Adding one character to a `String` value using `push` As a result, `s` will contain `lol`. @@ -486,7 +490,8 @@ let s2 = String::from("world!"); let s3 = s1 + &s2; // note s1 has been moved here and can no longer be used ``` -Using the `+` operator to combine two `String` values into a new `String` value +Listing 8-18: Using the `+` operator to combine two `String` values into a new +`String` value The string `s3` will contain `Hello, world!`. The reason `s1` is no longer valid after the addition, and the reason we used a reference to `s2`, has to do @@ -567,7 +572,7 @@ let s1 = String::from("hello"); let h = s1[0]; ``` -Attempting to use indexing syntax with a `String` +Listing 8-19: Attempting to use indexing syntax with a `String` This code will result in the following error: @@ -801,7 +806,7 @@ scores.insert(String::from("Blue"), 10); scores.insert(String::from("Yellow"), 50); ``` -Creating a new hash map and inserting some keys and values +Listing 8-20: Creating a new hash map and inserting some keys and values Note that we need to first `use` the `HashMap` from the collections portion of the standard library. Of our three common collections, this one is the least @@ -831,7 +836,7 @@ let team_name = String::from("Blue"); let score = scores.get(&team_name).copied().unwrap_or(0); ``` -Accessing the score for the Blue team stored in the hash map +Listing 8-21: Accessing the score for the Blue team stored in the hash map Here, `score` will have the value that’s associated with the Blue team, and the result will be `10`. The `get` method returns an `Option<&V>`; if there’s no @@ -881,7 +886,8 @@ map.insert(field_name, field_value); // using them and see what compiler error you get! ``` -Showing that keys and values are owned by the hash map once they’re inserted +Listing 8-22: Showing that keys and values are owned by the hash map once +they’re inserted We aren’t able to use the variables `field_name` and `field_value` after they’ve been moved into the hash map with the call to `insert`. @@ -924,7 +930,7 @@ scores.insert(String::from("Blue"), 25); println!("{:?}", scores); ``` -Replacing a value stored with a particular key +Listing 8-23: Replacing a value stored with a particular key This code will print `{"Blue": 25}`. The original value of `10` has been overwritten. @@ -955,7 +961,8 @@ scores.entry(String::from("Blue")).or_insert(50); println!("{:?}", scores); ``` -Using the `entry` method to only insert if the key does not already have a value +Listing 8-24: Using the `entry` method to only insert if the key does not +already have a value The `or_insert` method on `Entry` is defined to return a mutable reference to the value for the corresponding `Entry` key if that key exists, and if not, it @@ -993,7 +1000,8 @@ for word in text.split_whitespace() { println!("{:?}", map); ``` -Counting occurrences of words using a hash map that stores words and counts +Listing 8-25: Counting occurrences of words using a hash map that stores words +and counts 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 “Accessing diff --git a/nostarch/chapter09.md b/nostarch/chapter09.md index 37ad79dc..4aa81123 100644 --- a/nostarch/chapter09.md +++ b/nostarch/chapter09.md @@ -108,8 +108,8 @@ fn main() { } ``` -Attempting to access an element beyond the end of a vector, which will cause a -call to `panic!` +Listing 9-1: Attempting to access an element beyond the end of a vector, which +will cause a call to `panic!` Here, we’re attempting to access the 100th element of our vector (which is at index 99 because indexing starts at zero), but the vector has only three @@ -181,8 +181,8 @@ note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. ``` -The backtrace generated by a call to `panic!` displayed when the environment -variable `RUST_BACKTRACE` is set +Listing 9-2: The backtrace generated by a call to `panic!` displayed when the +environment variable `RUST_BACKTRACE` is set That’s a lot of output! The exact output you see might be different depending on your operating system and Rust version. In order to get backtraces with this @@ -243,7 +243,7 @@ fn main() { } ``` -Opening a file +Listing 9-3: Opening a file The return type of `File::open` is a `Result`. The generic parameter `T` has been filled in by the implementation of `File::open` with the type of the @@ -284,8 +284,8 @@ fn main() { } ``` -Using a `match` expression to handle the `Result` variants that might be -returned +Listing 9-4: Using a `match` expression to handle the `Result` variants that +might be returned Note that, like the `Option` enum, the `Result` enum and its variants have been brought into scope by the prelude, so we don’t need to specify `Result::` @@ -351,7 +351,7 @@ fn main() { } ``` -Handling different kinds of errors in different ways +Listing 9-5: Handling different kinds of errors in different ways The type of the value that `File::open` returns inside the `Err` variant is `io::Error`, which is a struct provided by the standard library. This struct @@ -501,7 +501,7 @@ use std::io::{self, Read}; } ``` -A function that returns errors to the calling code using `match` +Listing 9-6: A function that returns errors to the calling code using `match` This function can be written in a much shorter way, but we’re going to start by doing a lot of it manually in order to explore error handling; at the end, @@ -574,7 +574,8 @@ fn read_username_from_file() -> Result { } ``` -A function that returns errors to the calling code using the `?` operator +Listing 9-7: A function that returns errors to the calling code using the `?` +operator The `?` placed after a `Result` value is defined to work in almost the same way as the `match` expressions we defined to handle the `Result` values in Listing @@ -626,7 +627,7 @@ fn read_username_from_file() -> Result { } ``` -Chaining method calls after the `?` operator +Listing 9-8: Chaining method calls after the `?` operator We’ve moved the creation of the new `String` in `username` to the beginning of the function; that part hasn’t changed. Instead of creating a variable @@ -650,7 +651,8 @@ fn read_username_from_file() -> Result { } ``` -Using `fs::read_to_string` instead of opening and then reading the file +Listing 9-9: Using `fs::read_to_string` instead of opening and then reading the +file Reading a file into a string is a fairly common operation, so the standard library provides the convenient `fs::read_to_string` function that opens the @@ -683,8 +685,8 @@ fn main() { } ``` -Attempting to use the `?` in the `main` function that returns `()` won’t -compile. +Listing 9-10: Attempting to use the `?` in the `main` function that returns +`()` won’t compile. This code opens a file, which might fail. The `?` operator follows the `Result` value returned by `File::open`, but this `main` function has the return type of @@ -733,7 +735,7 @@ fn last_char_of_first_line(text: &str) -> Option { } ``` -Using the `?` operator on an `Option` value +Listing 9-11: Using the `?` operator on an `Option` value This function returns `Option` because it’s possible that there is a character there, but it’s also possible that there isn’t. This code takes the @@ -786,8 +788,8 @@ fn main() -> Result<(), Box> { } ``` -Changing `main` to return `Result<(), E>` allows the use of the `?` operator on -`Result` values. +Listing 9-12: Changing `main` to return `Result<(), E>` allows the use of the +`?` operator on `Result` values. The `Box` type is a *trait object*, which we’ll talk about in “Using Trait Objects That Allow for Values of Different Types” on page XX. For now, @@ -1024,7 +1026,8 @@ impl Guess { } ``` -A `Guess` type that will only continue with values between 1 and 100 +Listing 9-13: A `Guess` type that will only continue with values between 1 and +100 First we define a struct named `Guess` that has a field named `value` that holds an `i32` [1]. This is where the number will be stored. diff --git a/nostarch/chapter10.md b/nostarch/chapter10.md index 73c1e48d..dbe0c906 100644 --- a/nostarch/chapter10.md +++ b/nostarch/chapter10.md @@ -68,7 +68,7 @@ fn main() { } ``` -Finding the largest number in a list of numbers +Listing 10-1: Finding the largest number in a list of numbers We store a list of integers in the variable `number_list` [1] and place a reference to the first number in the list in a variable named `largest` [2]. We @@ -113,7 +113,7 @@ fn main() { } ``` -Code to find the largest number in *two* lists of numbers +Listing 10-2: Code to find the largest number in *two* lists of numbers Although this code works, duplicating code is tedious and error prone. We also have to remember to update the code in multiple places when we want to change @@ -157,7 +157,7 @@ fn main() { } ``` -Abstracted code to find the largest number in two lists +Listing 10-3: Abstracted code to find the largest number in two lists The `largest` function has a parameter called `list`, which represents any concrete slice of `i32` values we might pass into the function. As a result, @@ -236,8 +236,8 @@ fn main() { } ``` -Two functions that differ only in their names and in the types in their -signatures +Listing 10-4: Two functions that differ only in their names and in the types in +their signatures The `largest_i32` function is the one we extracted in Listing 10-3 that finds the largest `i32` in a slice. The `largest_char` function finds the largest @@ -300,7 +300,8 @@ fn main() { } ``` -The `largest` function using generic type parameters; this doesn’t compile yet +Listing 10-5: The `largest` function using generic type parameters; this +doesn’t compile yet If we compile this code right now, we’ll get this error: @@ -350,7 +351,7 @@ fn main() { } ``` -A `Point` struct that holds `x` and `y` values of type `T` +Listing 10-6: A `Point` struct that holds `x` and `y` values of type `T` The syntax for using generics in struct definitions is similar to that used in function definitions. First we declare the name of the type parameter inside @@ -377,8 +378,8 @@ fn main() { } ``` -The fields `x` and `y` must be the same type because both have the same generic -data type `T`. +Listing 10-7: The fields `x` and `y` must be the same type because both have +the same generic data type `T`. In this example, when we assign the integer value `5` to `x`, we let the compiler know that the generic type `T` will be an integer for this instance of @@ -414,8 +415,8 @@ fn main() { } ``` -A `Point` generic over two types so that `x` and `y` can be values of -different types +Listing 10-8: A `Point` generic over two types so that `x` and `y` can be +values of different types Now all the instances of `Point` shown are allowed! You can use as many generic type parameters in a definition as you want, but using more than a few makes @@ -493,8 +494,8 @@ fn main() { } ``` -Implementing a method named `x` on the `Point` struct that will return a -reference to the `x` field of type `T` +Listing 10-9: Implementing a method named `x` on the `Point` struct that +will return a reference to the `x` field of type `T` Here, we’ve defined a method named `x` on `Point` that returns a reference to the data in the field `x`. @@ -524,8 +525,8 @@ impl Point { } ``` -An `impl` block that only applies to a struct with a particular concrete type -for the generic type parameter `T` +Listing 10-10: An `impl` block that only applies to a struct with a particular +concrete type for the generic type parameter `T` This code means the type `Point` will have a `distance_from_origin` method; other instances of `Point` where `T` is not of type `f32` will not @@ -570,7 +571,8 @@ fn main() { } ``` -A method that uses generic types different from its struct’s definition +Listing 10-11: A method that uses generic types different from its struct’s +definition In `main`, we’ve defined a `Point` that has an `i32` for `x` (with value `5`) and an `f64` for `y` (with value `10.4` [3]). The `p2` variable is a `Point` @@ -682,7 +684,8 @@ pub trait Summary { } ``` -A `Summary` trait that consists of the behavior provided by a `summarize` method +Listing 10-12: A `Summary` trait that consists of the behavior provided by a +`summarize` method Here, we declare a trait using the `trait` keyword and then the trait’s name, which is `Summary` in this case. We also declare the trait as `pub` so that @@ -745,7 +748,8 @@ impl Summary for Tweet { } ``` -Implementing the `Summary` trait on the `NewsArticle` and `Tweet` types +Listing 10-13: Implementing the `Summary` trait on the `NewsArticle` and +`Tweet` types Implementing a trait on a type is similar to implementing regular methods. The difference is that after `impl`, we put the trait name we want to implement, @@ -823,8 +827,8 @@ pub trait Summary { } ``` -Defining a `Summary` trait with a default implementation of the `summarize` -method +Listing 10-14: Defining a `Summary` trait with a default implementation of the +`summarize` method To use a default implementation to summarize instances of `NewsArticle`, we specify an empty `impl` block with `impl Summary for NewsArticle {}`. @@ -1126,7 +1130,8 @@ impl Pair { } ``` -Conditionally implementing methods on a generic type depending on trait bounds +Listing 10-15: Conditionally implementing methods on a generic type depending +on trait bounds We can also conditionally implement a trait for any type that implements another trait. Implementations of a trait on any type that satisfies the trait @@ -1205,7 +1210,7 @@ fn main() { } ``` -An attempt to use a reference whose value has gone out of scope +Listing 10-16: An attempt to use a reference whose value has gone out of scope > Note: The examples in Listing 10-16, 10-17, and 10-23 declare variables without giving them an initial value, so the variable name exists in the outer @@ -1261,7 +1266,8 @@ fn main() { } // ---------+ ``` -Annotations of the lifetimes of `r` and `x`, named `'a` and `'b`, respectively +Listing 10-17: Annotations of the lifetimes of `r` and `x`, named `'a` and +`'b`, respectively Here, we’ve annotated the lifetime of `r` with `'a` and the lifetime of `x` with `'b`. As you can see, the inner `'b` block is much smaller than the outer @@ -1284,7 +1290,8 @@ fn main() { } // ----------+ ``` -A valid reference because the data has a longer lifetime than the reference +Listing 10-18: A valid reference because the data has a longer lifetime than +the reference 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 @@ -1313,8 +1320,8 @@ fn main() { } ``` -A `main` function that calls the `longest` function to find the longer of two -string slices +Listing 10-19: A `main` function that calls the `longest` function to find the +longer of two string slices 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 @@ -1337,8 +1344,8 @@ fn longest(x: &str, y: &str) -> &str { } ``` -An implementation of the `longest` function that returns the longer of two -string slices but does not yet compile +Listing 10-20: An implementation of the `longest` function that returns the +longer of two string slices but does not yet compile Instead, we get the following error that talks about lifetimes: @@ -1427,8 +1434,8 @@ fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { } ``` -The `longest` function definition specifying that all the references in the -signature must have the same lifetime `'a` +Listing 10-21: The `longest` function definition specifying that all the +references in the signature must have the same lifetime `'a` This code should compile and produce the result we want when we use it with the `main` function in Listing 10-19. @@ -1486,8 +1493,8 @@ fn main() { } ``` -Using the `longest` function with references to `String` values that have -different concrete lifetimes +Listing 10-22: Using the `longest` function with references to `String` values +that have different concrete lifetimes In this example, `string1` is valid until the end of the outer scope, `string2` is valid until the end of the inner scope, and `result` references something @@ -1517,7 +1524,7 @@ fn main() { } ``` -Attempting to use `result` after `string2` has gone out of scope +Listing 10-23: Attempting to use `result` after `string2` has gone out of scope When we try to compile this code, we get this error: @@ -1645,7 +1652,7 @@ fn main() { } ``` -A struct that holds a reference, requiring a lifetime annotation +Listing 10-24: A struct that holds a reference, requiring a lifetime annotation This struct has the single field `part` that holds a string slice, which is a reference [2]. As with generic data types, we declare the name of the generic @@ -1684,8 +1691,8 @@ fn first_word(s: &str) -> &str { } ``` -A function we defined in Listing 4-9 that compiled without lifetime -annotations, even though the parameter and return type are references +Listing 10-25: A function we defined in Listing 4-9 that compiled without +lifetime annotations, even though the parameter and return type are references The reason this function compiles without lifetime annotations is historical: in early versions (pre-1.0) of Rust, this code wouldn’t have compiled because diff --git a/nostarch/chapter11.md b/nostarch/chapter11.md index 3b3ec164..cf909d70 100644 --- a/nostarch/chapter11.md +++ b/nostarch/chapter11.md @@ -98,7 +98,8 @@ mod tests { } ``` -The test module and function generated automatically by `cargo new` +Listing 11-1: The test module and function generated automatically by `cargo +new` For now, let’s ignore the top two lines and focus on the function. Note the `#[test]` annotation [1]: this attribute indicates this is a test function, so @@ -136,7 +137,7 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` -The output from running the automatically generated test +Listing 11-2: The output from running the automatically generated test Cargo compiled and ran the test. We see the line `running 1 test` [1]. The next line shows the name of the generated test function, called `it_works`, and that @@ -216,7 +217,8 @@ mod tests { } ``` -Adding a second test that will fail because we call the `panic!` macro +Listing 11-3: Adding a second test that will fail because we call the `panic!` +macro Run the tests again using `cargo test`. The output should look like Listing 11-4, which shows that our `exploration` test passed and `another` failed. @@ -242,7 +244,7 @@ filtered out; finished in 0.00s error: test failed, to rerun pass '--lib' ``` -Test results when one test passes and one test fails +Listing 11-4: Test results when one test passes and one test fails Instead of `ok`, the line `test tests::another` shows `FAILED` [1]. Two new sections appear between the individual results and the summary: the first [2] @@ -289,7 +291,8 @@ impl Rectangle { } ``` -Using the `Rectangle` struct and its `can_hold` method from Chapter 5 +Listing 11-5: Using the `Rectangle` struct and its `can_hold` method from +Chapter 5 The `can_hold` method returns a Boolean, which means it’s a perfect use case for the `assert!` macro. In Listing 11-6, we write a test that exercises the @@ -320,8 +323,8 @@ mod tests { } ``` -A test for `can_hold` that checks whether a larger rectangle can indeed hold a -smaller rectangle +Listing 11-6: A test for `can_hold` that checks whether a larger rectangle can +indeed hold a smaller rectangle Note that we’ve added a new line inside the `tests` module: `use super::*;` [1]. The `tests` module is a regular module that follows the usual visibility @@ -465,7 +468,7 @@ mod tests { } ``` -Testing the function `add_two` using the `assert_eq!` macro +Listing 11-7: Testing the function `add_two` using the `assert_eq!` macro Let’s check that it passes! @@ -692,7 +695,7 @@ mod tests { } ``` -Testing that a condition will cause a panic! +Listing 11-8: Testing that a condition will cause a panic! We place the `#[should_panic]` attribute after the `#[test]` attribute and before the test function it applies to. Let’s look at the result when this test @@ -792,7 +795,8 @@ mod tests { } ``` -Testing for a `panic!` with a panic message containing a specified substring +Listing 11-9: Testing for a `panic!` with a panic message containing a +specified substring This test will pass because the value we put in the `should_panic` attribute’s `expected` parameter is a substring of the message that the `Guess::new` @@ -980,7 +984,7 @@ mod tests { } ``` -Tests for a function that calls `println!` +Listing 11-10: Tests for a function that calls `println!` When we run these tests with `cargo test`, we’ll see the following output: @@ -1091,7 +1095,7 @@ mod tests { } ``` -Three tests with three different names +Listing 11-11: Three tests with three different names If we run the tests without passing any arguments, as we saw earlier, all the tests will run in parallel: @@ -1310,7 +1314,7 @@ mod tests { } ``` -Testing a private function +Listing 11-12: Testing a private function 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 @@ -1364,7 +1368,7 @@ fn it_adds_two() { } ``` -An integration test of a function in the `adder` crate +Listing 11-13: An integration test of a function in the `adder` crate Each file in the *tests* directory is a separate crate, so we need to bring our library into each test crate’s scope. For that reason we add `use adder;` at diff --git a/nostarch/chapter12.md b/nostarch/chapter12.md index 6a082c12..19aae1ad 100644 --- a/nostarch/chapter12.md +++ b/nostarch/chapter12.md @@ -98,7 +98,8 @@ fn main() { } ``` -Collecting the command line arguments into a vector and printing them +Listing 12-1: Collecting the command line arguments into a vector and printing +them First we bring the `std::env` module into scope with a `use` statement so we can use its `args` function. Notice that the `std::env::args` function is @@ -176,7 +177,8 @@ fn main() { } ``` -Creating variables to hold the query argument and file path argument +Listing 12-2: Creating variables to hold the query argument and file path +argument As we saw when we printed the vector, the program’s name takes up the first value in the vector at `args[0]`, so we’re starting arguments at index 1. The @@ -227,7 +229,7 @@ To tell your name the livelong day To an admiring bog! ``` -A poem by Emily Dickinson makes a good test case. +Listing 12-3: A poem by Emily Dickinson makes a good test case. With the text in place, edit *src/main.rs* and add code to read the file, as shown in Listing 12-4. @@ -249,7 +251,7 @@ fn main() { } ``` -Reading the contents of the file specified by the second argument +Listing 12-4: Reading the contents of the file specified by the second argument First we bring in a relevant part of the standard library with a `use` statement: we need `std::fs` to handle files [1]. @@ -385,7 +387,7 @@ fn parse_config(args: &[String]) -> (&str, &str) { } ``` -Extracting a `parse_config` function from `main` +Listing 12-5: Extracting a `parse_config` function from `main` We’re still collecting the command line arguments into a vector, but instead of assigning the argument value at index 1 to the variable `query` and the @@ -450,7 +452,8 @@ fn main() { } ``` -Refactoring `parse_config` to return an instance of a `Config` struct +Listing 12-6: Refactoring `parse_config` to return an instance of a `Config` +struct We’ve added a struct named `Config` defined to have fields named `query` and `file_path` [5]. The signature of `parse_config` now indicates that it returns @@ -533,7 +536,7 @@ fn main() { } ``` -Changing `parse_config` into `Config::new` +Listing 12-7: Changing `parse_config` into `Config::new` We’ve updated `main` where we were calling `parse_config` to instead call `Config::new` [1]. We’ve changed the name of `parse_config` to `new` [3] and @@ -579,7 +582,7 @@ fn new(args: &[String]) -> Config { --snip-- ``` -Adding a check for the number of arguments +Listing 12-8: Adding a check for the number of arguments This code is similar to the `Guess::new` function we wrote in Listing 9-13, where we called `panic!` when the `value` argument was out of the range of @@ -644,7 +647,7 @@ impl Config { } ``` -Returning a `Result` from `Config::build` +Listing 12-9: Returning a `Result` from `Config::build` Our `build` function returns a `Result` with a `Config` instance in the success case and an `&'static str` in the error case. Our error values will always be @@ -684,7 +687,7 @@ fn main() { --snip-- ``` -Exiting with an error code if building a `Config` fails +Listing 12-10: Exiting with an error code if building a `Config` fails In this listing, we’ve used a method we haven’t covered in detail yet: `unwrap_or_else`, which is defined on `Result` by the standard library @@ -754,7 +757,8 @@ fn run(config: Config) { --snip-- ``` -Extracting a `run` function containing the rest of the program logic +Listing 12-11: Extracting a `run` function containing the rest of the program +logic The `run` function now contains all the remaining logic from `main`, starting from reading the file. The `run` function takes the `Config` instance as an @@ -786,7 +790,7 @@ Filename: src/main.rs } ``` -Changing the `run` function to return `Result` +Listing 12-12: Changing the `run` function to return `Result` We’ve made three significant changes here. First, we changed the return type of the `run` function to `Result<(), Box>` [2]. This function @@ -904,7 +908,7 @@ pub fn run(config: Config) -> Result<(), Box> { } ``` -Moving `Config` and `run` into *src/lib.rs* +Listing 12-13: Moving `Config` and `run` into *src/lib.rs* We’ve made liberal use of the `pub` keyword: on `Config`, on its fields and its `build` method, and on the `run` function. We now have a library crate that has @@ -929,7 +933,7 @@ fn main() { } ``` -Using the `minigrep` library crate in *src/main.rs* +Listing 12-14: Using the `minigrep` library crate in *src/main.rs* We add a `use minigrep::Config` line to bring the `Config` type from the library crate into the binary crate’s scope, and we prefix the `run` function @@ -1003,7 +1007,7 @@ Pick three."; } ``` -Creating a failing test for the `search` function we wish we had +Listing 12-15: Creating a failing test for the `search` function we wish we had This test searches for the string `"duct"`. The text we’re searching is three lines, only one of which contains `"duct"` (note that the backslash after the @@ -1030,7 +1034,8 @@ pub fn search<'a>( } ``` -Defining just enough of the `search` function so our test will compile +Listing 12-16: Defining just enough of the `search` function so our test will +compile Notice that we need to define an explicit lifetime `'a` in the signature of `search` and use that lifetime with the `contents` argument and the return @@ -1144,7 +1149,7 @@ pub fn search<'a>( } ``` -Iterating through each line in `contents` +Listing 12-17: Iterating through each line in `contents` The `lines` method returns an iterator. We’ll talk about iterators in depth in Chapter 13, but recall that you saw this way of using an iterator in Listing @@ -1173,7 +1178,8 @@ pub fn search<'a>( } ``` -Adding functionality to see whether the line contains the string in `query` +Listing 12-18: Adding functionality to see whether the line contains the string +in `query` At the moment, we’re building up functionality. To get the code to compile, we need to return a value from the body as we indicated we would in the function @@ -1205,7 +1211,7 @@ pub fn search<'a>( } ``` -Storing the lines that match so we can return them +Listing 12-19: Storing the lines that match so we can return them Now the `search` function should return only the lines that contain `query`, and our test should pass. Let’s run the test: @@ -1348,7 +1354,8 @@ Trust me."; } ``` -Adding a new failing test for the case-insensitive function we’re about to add +Listing 12-20: Adding a new failing test for the case-insensitive function +we’re about to add Note that we’ve edited the old test’s `contents` too. We’ve added a new line with the text `"Duct tape."` using a capital *D* that shouldn’t match the query @@ -1393,8 +1400,8 @@ pub fn search_case_insensitive<'a>( } ``` -Defining the `search_case_insensitive` function to lowercase the query and the -line before comparing them +Listing 12-21: Defining the `search_case_insensitive` function to lowercase the +query and the line before comparing them First we lowercase the `query` string and store it in a shadowed variable with the same name [1]. Calling `to_lowercase` on the query is necessary so that no @@ -1469,8 +1476,8 @@ pub fn run(config: Config) -> Result<(), Box> { } ``` -Calling either `search` or `search_case_insensitive` based on the value in -`config.ignore_case` +Listing 12-22: Calling either `search` or `search_case_insensitive` based on +the value in `config.ignore_case` Finally, we need to check for the environment variable. The functions for working with environment variables are in the `env` module in the standard @@ -1507,7 +1514,8 @@ impl Config { } ``` -Checking for any value in an environment variable named `IGNORE_CASE` +Listing 12-23: Checking for any value in an environment variable named +`IGNORE_CASE` Here, we create a new variable, `ignore_case`. To set its value, we call the `env::var` function and pass it the name of the `IGNORE_CASE` environment @@ -1661,8 +1669,8 @@ fn main() { } ``` -Writing error messages to standard error instead of standard output using -`eprintln!` +Listing 12-24: Writing error messages to standard error instead of standard +output using `eprintln!` Let’s now run the program again in the same way, without any arguments and redirecting standard output with `>`: diff --git a/nostarch/chapter14.md b/nostarch/chapter14.md index 76fe2bf7..f5f2be71 100644 --- a/nostarch/chapter14.md +++ b/nostarch/chapter14.md @@ -17,6 +17,7 @@ its other, more advanced features to show you how to do the following: * Organize large projects with workspaces. * Install binaries from *https://crates.io*. * Extend Cargo using custom commands. + Cargo can do even more than the functionality we cover in this chapter, so for a full explanation of all its features, see its documentation at *https://doc.rust-lang.org/cargo*. @@ -37,17 +38,8 @@ These profile names might be familiar from the output of your builds: ``` $ cargo build -``` - -``` Finished dev [unoptimized + debuginfo] target(s) in 0.0s -``` - -``` $ cargo build --release -``` - -``` Finished release [optimized] target(s) in 0.0s ``` @@ -63,21 +55,9 @@ Filename: Cargo.toml ``` [profile.dev] -``` - -``` opt-level = 0 -``` -``` - -``` - -``` [profile.release] -``` - -``` opt-level = 3 ``` @@ -100,9 +80,6 @@ Filename: Cargo.toml ``` [profile.dev] -``` - -``` opt-level = 1 ``` @@ -146,57 +123,21 @@ Filename: src/lib.rs ``` /// Adds one to the number given. -``` - -``` /// -``` - -``` /// # Examples -``` - -``` /// -``` - -``` /// ``` -``` - -``` /// let arg = 5; -``` - -``` /// let answer = my_crate::add_one(arg); -``` - -``` /// -``` - -``` /// assert_eq!(6, answer); -``` - -``` /// ``` -``` - -``` pub fn add_one(x: i32) -> i32 { -``` - -``` x + 1 -``` - -``` } ``` -A documentation comment for a function +Listing 14-1: A documentation comment for a function Here, we give a description of what the `add_one` function does, start a section with the heading `Examples`, and then provide code that demonstrates @@ -211,30 +152,27 @@ crate’s dependencies) and open the result in a web browser. Navigate to the `add_one` function and you’ll see how the text in the documentation comments is rendered, as shown in Figure 14-1. +Figure 14-1: HTML documentation for the `add_one` function -Unmatched: GraphicSlug - -Unmatched: CaptionLine - #### Commonly Used Sections +#### Commonly Used Sections We used the `# Examples` Markdown heading in Listing 14-1 to create a section in the HTML with the title “Examples.” Here are some other sections that crate authors commonly use in their documentation: +* **Panics**: The scenarios in which the function being documented could panic. +Callers of the function who don’t want their programs to panic should make sure +they don’t call the function in these situations. +* **Errors**: If the function returns a `Result`, describing the kinds of +errors that might occur and what conditions might cause those errors to be +returned can be helpful to callers so they can write code to handle the +different kinds of errors in different ways. +* **Safety**: If the function is `unsafe` to call (we discuss unsafety in +Chapter 19), there should be a section explaining why the function is unsafe +and covering the invariants that the function expects callers to uphold. -Unmatched: RunInHead - -Unmatched: RunInPara - -Unmatched: RunInHead - -Unmatched: RunInPara - -Unmatched: RunInHead - -Unmatched: RunInPara - Most documentation comments don’t need all of these sections, but this is -a good checklist to remind you of the aspects of your code users will be +Most documentation comments don’t need all of these sections, but this is a +good checklist to remind you of the aspects of your code users will be interested in knowing about. #### Documentation Comments as Tests @@ -250,29 +188,11 @@ looks like this: ``` Doc-tests my_crate -``` -``` - -``` - -``` running 1 test -``` - -``` test src/lib.rs - add_one (line 5) ... ok -``` -``` - -``` - -``` test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 -``` - -``` filtered out; finished in 0.27s ``` @@ -296,33 +216,15 @@ Filename: src/lib.rs ``` //! # My Crate -``` - -``` //! -``` - -``` //! `my_crate` is a collection of utilities to make performing -``` - -``` //! certain calculations more convenient. -``` -``` - -``` - -``` /// Adds one to the number given. -``` - -``` --snip-- ``` -Documentation for the `my_crate` crate as a whole +Listing 14-2: Documentation for the `my_crate` crate as a whole Notice there isn’t any code after the last line that begins with `//!`. Because we started the comments with `//!` instead of `///`, we’re documenting the item @@ -334,11 +236,10 @@ When we run `cargo doc --open`, these comments will display on the front page of the documentation for `my_crate` above the list of public items in the crate, as shown in Figure 14-2. +Figure 14-2: Rendered documentation for `my_crate`, including the comment +describing the crate as a whole -Unmatched: GraphicSlug - -Unmatched: CaptionLine - Documentation comments within items are useful for describing crates and +Documentation comments within items are useful for describing crates and modules especially. Use them to explain the overall purpose of the container to help your users understand the crate’s organization. @@ -356,8 +257,8 @@ convenient for your users. You might want to organize your structs in a hierarchy containing multiple levels, but then people who want to use a type you’ve defined deep in the hierarchy might have trouble finding out that type exists. They might also be annoyed at having to enter `use` -`my_crate``::`some_module`::`another_module`::`UsefulType`;` rather than `use` -`my_crate``::`UsefulType`;`. +`my_crate::`some_module`::`another_module`::`UsefulType`;` rather than `use` +`my_crate::`UsefulType`;`. The good news is that if the structure *isn’t* convenient for others to use from another library, you don’t have to rearrange your internal organization: @@ -375,144 +276,51 @@ Filename: src/lib.rs ``` //! # Art -``` - -``` //! -``` - -``` //! A library for modeling artistic concepts. -``` -``` - -``` - -``` pub mod kinds { -``` - -``` /// The primary colors according to the RYB color model. -``` - -``` pub enum PrimaryColor { -``` - -``` Red, -``` - -``` Yellow, -``` - -``` Blue, -``` - -``` } -``` -``` - -``` - -``` /// The secondary colors according to the RYB color model. -``` - -``` pub enum SecondaryColor { -``` - -``` Orange, -``` - -``` Green, -``` - -``` Purple, -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` pub mod utils { -``` - -``` use crate::kinds::*; -``` -``` - -``` - -``` /// Combines two primary colors in equal amounts to create -``` - -``` /// a secondary color. -``` - -``` pub fn mix( -``` - -``` c1: PrimaryColor, -``` - -``` c2: PrimaryColor, -``` - -``` ) -> SecondaryColor { -``` - -``` --snip-- -``` - -``` } -``` - -``` } ``` -An `art` library with items organized into `kinds` and `utils` modules +Listing 14-3: An `art` library with items organized into `kinds` and `utils` +modules Figure 14-3 shows what the front page of the documentation for this crate generated by `cargo doc` would look like. +Figure 14-3: Front page of the documentation for `art` that lists the `kinds` +and `utils` modules -Unmatched: GraphicSlug - -Unmatched: CaptionLine - Note that the `PrimaryColor` and `SecondaryColor` types aren’t listed on -the front page, nor is the `mix` function. We have to click `kinds` and `utils` -to see them. +Note that the `PrimaryColor` and `SecondaryColor` types aren’t listed on the +front page, nor is the `mix` function. We have to click `kinds` and `utils` to +see them. Another crate that depends on this library would need `use` statements that bring the items from `art` into scope, specifying the module structure that’s @@ -523,37 +331,17 @@ Filename: src/main.rs ``` use art::kinds::PrimaryColor; -``` - -``` use art::utils::mix; -``` -``` - -``` - -``` fn main() { -``` - -``` let red = PrimaryColor::Red; -``` - -``` let yellow = PrimaryColor::Yellow; -``` - -``` mix(red, yellow); -``` - -``` } ``` -A crate using the `art` crate’s items with its internal structure exported +Listing 14-4: A crate using the `art` crate’s items with its internal structure +exported The author of the code in Listing 14-4, which uses the `art` crate, had to figure out that `PrimaryColor` is in the `kinds` module and `mix` is in the @@ -572,105 +360,47 @@ Filename: src/lib.rs ``` //! # Art -``` - -``` //! -``` - -``` //! A library for modeling artistic concepts. -``` -``` - -``` - -``` pub use self::kinds::PrimaryColor; -``` - -``` pub use self::kinds::SecondaryColor; -``` - -``` pub use self::utils::mix; -``` -``` - -``` - -``` pub mod kinds { -``` - -``` --snip-- -``` - -``` } -``` -``` - -``` - -``` pub mod utils { -``` - -``` --snip-- -``` - -``` } ``` -Adding `pub use` statements to re-export items +Listing 14-5: Adding `pub use` statements to re-export items The API documentation that `cargo doc` generates for this crate will now list and link re-exports on the front page, as shown in Figure 14-4, making the `PrimaryColor` and `SecondaryColor` types and the `mix` function easier to find. +Figure 14-4: The front page of the documentation for `art` that lists the +re-exports -Unmatched: GraphicSlug - -Unmatched: CaptionLine - The `art` crate users can still see and use the internal structure from -Listing 14-3 as demonstrated in Listing 14-4, or they can use the more -convenient structure in Listing 14-5, as shown in Listing 14-6. +The `art` crate users can still see and use the internal structure from Listing +14-3 as demonstrated in Listing 14-4, or they can use the more convenient +structure in Listing 14-5, as shown in Listing 14-6. Filename: src/main.rs ``` use art::mix; -``` - -``` use art::PrimaryColor; -``` -``` - -``` - -``` fn main() { -``` - -``` --snip-- -``` - -``` } ``` -A program using the re-exported items from the `art` crate +Listing 14-6: A program using the re-exported items from the `art` crate In cases where there are many nested modules, re-exporting the types at the top level with `pub use` can make a significant difference in the experience of @@ -692,7 +422,7 @@ Before you can publish any crates, you need to create an account on *https://crates.io* and log in via a GitHub account. (The GitHub account is currently a requirement, but the site might support other ways of creating an account in the future.) Once you’re logged in, visit your account settings at -*https://crates.io/**me* and retrieve your API key. Then run the `cargo login` +*https://crates.io/me* and retrieve your API key. Then run the `cargo login` command with your API key, like this: ``` @@ -723,9 +453,6 @@ Filename: Cargo.toml ``` [package] -``` - -``` name = "guessing_game" ``` @@ -734,53 +461,17 @@ the crate at this point, you’ll get a warning and then an error: ``` $ cargo publish -``` - -``` Updating crates.io index -``` - -``` warning: manifest has no description, license, license-file, documentation, -``` - -``` homepage or repository. -``` - -``` See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata -``` - -``` for more info. -``` - -``` --snip-- -``` - -``` error: failed to publish to registry at https://crates.io -``` -``` - -``` - -``` Caused by: -``` - -``` the remote server responded with an error: missing or empty metadata fields: -``` - -``` description, license. Please see https://doc.rust- -``` - -``` lang.org/cargo/reference/manifest.html for how to upload metadata ``` @@ -798,13 +489,7 @@ Filename: Cargo.toml ``` [package] -``` - -``` name = "guessing_game" -``` - -``` license = "MIT" ``` @@ -826,37 +511,13 @@ Filename: Cargo.toml ``` [package] -``` - -``` name = "guessing_game" -``` - -``` version = "0.1.0" -``` - -``` edition = "2021" -``` - -``` description = "A fun game where you guess what number the -``` - -``` computer has chosen." -``` - -``` license = "MIT OR Apache-2.0" -``` -``` - -``` - -``` [dependencies] ``` @@ -882,33 +543,12 @@ Run the `cargo publish` command again. It should succeed now: ``` $ cargo publish -``` - -``` Updating crates.io index -``` - -``` Packaging guessing_game v0.1.0 (file:///projects/guessing_game) -``` - -``` Verifying guessing_game v0.1.0 (file:///projects/guessing_game) -``` - -``` Compiling guessing_game v0.1.0 -``` - -``` (file:///projects/guessing_game/target/package/guessing_game-0.1.0) -``` - -``` Finished dev [unoptimized + debuginfo] target(s) in 0.19s -``` - -``` Uploading guessing_game v0.1.0 (file:///projects/guessing_game) ``` @@ -943,13 +583,7 @@ run: ``` $ cargo yank --vers 1.0.1 -``` - -``` Updating crates.io index -``` - -``` Yank guessing_game@1.0.1 ``` @@ -958,13 +592,7 @@ to start depending on a version again: ``` $ cargo yank --vers 1.0.1 --undo -``` - -``` Updating crates.io index -``` - -``` Unyank guessing_game@1.0.1 ``` @@ -993,9 +621,6 @@ a new directory for the workspace: ``` $ mkdir add -``` - -``` $ cd add ``` @@ -1009,21 +634,9 @@ Filename: Cargo.toml ``` [workspace] -``` -``` - -``` - -``` members = [ -``` - -``` "adder", -``` - -``` ] ``` @@ -1032,9 +645,6 @@ Next, we’ll create the `adder` binary crate by running `cargo new` within the ``` $ cargo new adder -``` - -``` Created binary (application) `adder` package ``` @@ -1043,29 +653,11 @@ in your *add* directory should look like this: ``` ├── Cargo.lock -``` - -``` ├── Cargo.toml -``` - -``` ├── adder -``` - -``` │ ├── Cargo.toml -``` - -``` │ └── src -``` - -``` │ └── main.rs -``` - -``` └── target ``` @@ -1090,25 +682,10 @@ Filename: Cargo.toml ``` [workspace] -``` -``` - -``` - -``` members = [ -``` - -``` "adder", -``` - -``` "add_one", -``` - -``` ] ``` @@ -1116,9 +693,6 @@ Then generate a new library crate named `add_one`: ``` $ cargo new add_one --lib -``` - -``` Created library `add_one` package ``` @@ -1126,45 +700,15 @@ Your *add* directory should now have these directories and files: ``` ├── Cargo.lock -``` - -``` ├── Cargo.toml -``` - -``` ├── add_one -``` - -``` │ ├── Cargo.toml -``` - -``` │ └── src -``` - -``` │ └── lib.rs -``` - -``` ├── adder -``` - -``` │ ├── Cargo.toml -``` - -``` │ └── src -``` - -``` │ └── main.rs -``` - -``` └── target ``` @@ -1174,13 +718,7 @@ Filename: add_one/src/lib.rs ``` pub fn add_one(x: i32) -> i32 { -``` - -``` x + 1 -``` - -``` } ``` @@ -1192,9 +730,6 @@ Filename: adder/Cargo.toml ``` [dependencies] -``` - -``` add_one = { path = "../add_one" } ``` @@ -1210,58 +745,25 @@ Filename: adder/src/main.rs ``` use add_one; -``` -``` - -``` - -``` fn main() { -``` - -``` let num = 10; -``` - -``` println!( -``` - -``` "Hello, world! {num} plus one is {}!", -``` - -``` add_one::add_one(num) -``` - -``` ); -``` - -``` } ``` -Using the `add_one` library crate from the `adder` crate +Listing 14-7: Using the `add_one` library crate from the `adder` crate Let’s build the workspace by running `cargo build` in the top-level *add* directory! ``` $ cargo build -``` - -``` Compiling add_one v0.1.0 (file:///projects/add/add_one) -``` - -``` Compiling adder v0.1.0 (file:///projects/add/adder) -``` - -``` Finished dev [unoptimized + debuginfo] target(s) in 0.68s ``` @@ -1271,17 +773,8 @@ with `cargo run`: ``` $ cargo run -p adder -``` - -``` Finished dev [unoptimized + debuginfo] target(s) in 0.0s -``` - -``` Running `target/debug/adder` -``` - -``` Hello, world! 10 plus one is 11! ``` @@ -1303,9 +796,6 @@ Filename: add_one/Cargo.toml ``` [dependencies] -``` - -``` rand = "0.8.5" ``` @@ -1316,33 +806,12 @@ referring to the `rand` we brought into scope: ``` $ cargo build -``` - -``` Updating crates.io index -``` - -``` Downloaded rand v0.8.5 -``` - -``` --snip-- -``` - -``` Compiling rand v0.8.5 -``` - -``` Compiling add_one v0.1.0 (file:///projects/add/add_one) -``` - -``` Compiling adder v0.1.0 (file:///projects/add/adder) -``` - -``` Finished dev [unoptimized + debuginfo] target(s) in 10.18s ``` @@ -1354,33 +823,12 @@ to the *adder/src/main.rs* file for the `adder` package, we’ll get an error: ``` $ cargo build -``` - -``` --snip-- -``` - -``` Compiling adder v0.1.0 (file:///projects/add/adder) -``` - -``` error[E0432]: unresolved import `rand` -``` - -``` --> adder/src/main.rs:2:5 -``` - -``` | -``` - -``` 2 | use rand; -``` - -``` | ^^^^ no external crate `rand` ``` @@ -1401,53 +849,17 @@ Filename: add_one/src/lib.rs ``` pub fn add_one(x: i32) -> i32 { -``` - -``` x + 1 -``` - -``` } -``` -``` - -``` - -``` #[cfg(test)] -``` - -``` mod tests { -``` - -``` use super::*; -``` -``` - -``` - -``` #[test] -``` - -``` fn it_works() { -``` - -``` assert_eq!(3, add_one(2)); -``` - -``` } -``` - -``` } ``` @@ -1457,101 +869,29 @@ the workspace: ``` $ cargo test -``` - -``` Compiling add_one v0.1.0 (file:///projects/add/add_one) -``` - -``` Compiling adder v0.1.0 (file:///projects/add/adder) -``` - -``` Finished test [unoptimized + debuginfo] target(s) in 0.27s -``` - -``` Running unittests src/lib.rs (target/debug/deps/add_one-f0253159197f7841) -``` -``` - -``` - -``` running 1 test -``` - -``` test tests::it_works ... ok -``` -``` - -``` - -``` test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; -``` - -``` finished in 0.00s -``` -``` - -``` - -``` Running unittests src/main.rs (target/debug/deps/adder-49979ff40686fa8e) -``` -``` - -``` - -``` running 0 tests -``` -``` - -``` - -``` test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; -``` - -``` finished in 0.00s -``` -``` - -``` - -``` Doc-tests add_one -``` -``` - -``` - -``` running 0 tests -``` -``` - -``` - -``` test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; -``` - -``` finished in 0.00s ``` @@ -1566,65 +906,20 @@ we want to test: ``` $ cargo test -p add_one -``` - -``` Finished test [unoptimized + debuginfo] target(s) in 0.00s -``` - -``` Running unittests src/lib.rs (target/debug/deps/add_one-b3235fea9a156f74) -``` -``` - -``` - -``` running 1 test -``` - -``` test tests::it_works ... ok -``` -``` - -``` - -``` test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; -``` - -``` finished in 0.00s -``` -``` - -``` - -``` Doc-tests add_one -``` -``` - -``` - -``` running 0 tests -``` -``` - -``` - -``` test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; -``` - -``` finished in 0.00s ``` @@ -1668,41 +963,14 @@ can run the following: ``` $ cargo install ripgrep -``` - -``` Updating crates.io index -``` - -``` Downloaded ripgrep v13.0.0 -``` - -``` Downloaded 1 crate (243.3 KB) in 0.88s -``` - -``` Installing ripgrep v13.0.0 -``` - -``` --snip-- -``` - -``` Compiling ripgrep v13.0.0 -``` - -``` Finished release [optimized + debuginfo] target(s) in 3m 10s -``` - -``` Installing ~/.cargo/bin/rg -``` - -``` Installed package `ripgrep v13.0.0` (executable `rg`) ``` diff --git a/nostarch/chapter15.md b/nostarch/chapter15.md index ca37e727..c0e847da 100644 --- a/nostarch/chapter15.md +++ b/nostarch/chapter15.md @@ -53,6 +53,7 @@ cover the most common smart pointers in the standard library: * `Rc`, a reference counting type that enables multiple ownership * `Ref` and `RefMut`, accessed through `RefCell`, a type that enforces the borrowing rules at runtime instead of compile time + In addition, we’ll cover the *interior mutability* pattern where an immutable type exposes an API for mutating an interior value. We’ll also discuss *reference cycles*: how they can leak memory and how to prevent them. @@ -76,6 +77,7 @@ to use a value of that type in a context that requires an exact size ensure the data won’t be copied when you do so * When you want to own a value and you care only that it’s a type that implements a particular trait rather than being of a specific type + We’ll demonstrate the first situation in “Enabling Recursive Types with Boxes” on page XX. In the second case, transferring ownership of a large amount of data can take a long time because the data is copied around on the stack. To @@ -97,21 +99,12 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let b = Box::new(5); -``` - -``` println!("b = {b}"); -``` - -``` } ``` -Storing an `i32` value on the heap using a box +Listing 15-1: Storing an `i32` value on the heap using a box 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 @@ -146,8 +139,8 @@ more complex situations involving recursive types. A *cons list* is a data structure that comes from the Lisp programming language and its dialects, is made up of nested pairs, and is the Lisp version of a -linked list. Its name comes from the `cons` function (short for *construct* -*function*) in Lisp that constructs a new pair from its two arguments. By +linked list. Its name comes from the `cons` function (short for *construct +function*) in Lisp that constructs a new pair from its two arguments. By calling `cons` on a pair consisting of a value and another pair, we can construct cons lists made up of recursive pairs. @@ -179,24 +172,15 @@ Filename: src/main.rs ``` enum List { -``` - -``` Cons(i32, List), -``` - -``` Nil, -``` - -``` } ``` -The first attempt at defining an enum to represent a cons list data structure -of `i32` values +Listing 15-2: The first attempt at defining an enum to represent a cons list +data structure of `i32` values -> NoteWe’re implementing a cons list that holds only `i32` values for the +> Note: We’re implementing a cons list that holds only `i32` values for the purposes of this example. We could have implemented it using generics, as we discussed in Chapter 10, to define a cons list type that could store values of any type. @@ -208,33 +192,15 @@ Filename: src/main.rs ``` --snip-- -``` -``` - -``` - -``` use crate::List::{Cons, Nil}; -``` -``` - -``` - -``` fn main() { -``` - -``` let list = Cons(1, Cons(2, Cons(3, Nil))); -``` - -``` } ``` -Using the `List` enum to store the list `1, 2, 3` +Listing 15-3: Using the `List` enum to store the list `1, 2, 3` The first `Cons` value holds `1` and another `List` value. This `List` value is another `Cons` value that holds `2` and another `List` value. This `List` value @@ -246,57 +212,21 @@ Listing 15-4. ``` error[E0072]: recursive type `List` has infinite size -``` - -``` --> src/main.rs:1:1 -``` - -``` | -``` - -``` 1 | enum List { -``` - -``` | ^^^^^^^^^ recursive type has infinite size -``` - -``` 2 | Cons(i32, List), -``` - -``` | ---- recursive without indirection -``` - -``` | -``` - -``` help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List` -``` - -``` representable -``` - -``` | -``` - -``` 2 | Cons(i32, Box), -``` - -``` | ++++ + ``` -The error we get when attempting to define a recursive enum +Listing 15-4: The error we get when attempting to define a recursive enum The error shows this type “has infinite size.” The reason is that we’ve defined `List` with a variant that is recursive: it holds another value of itself @@ -311,25 +241,10 @@ definitions in Chapter 6: ``` enum Message { -``` - -``` Quit, -``` - -``` Move { x: i32, y: i32 }, -``` - -``` Write(String), -``` - -``` ChangeColor(i32, i32, i32), -``` - -``` } ``` @@ -349,32 +264,18 @@ type needs, the compiler looks at the variants, starting with the `Cons` variant. The `Cons` variant holds a value of type `i32` and a value of type `List`, and this process continues infinitely, as shown in Figure 15-1. +Figure 15-1: An infinite `List` consisting of infinite `Cons` variants -Unmatched: GraphicSlug - -Unmatched: CaptionLine - #### Using Box to Get a Recursive Type with a Known Size +#### Using Box to Get a Recursive Type with a Known Size Because Rust can’t figure out how much space to allocate for recursively defined types, the compiler gives an error with this helpful suggestion: ``` help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List` -``` - -``` representable -``` - -``` | -``` - -``` 2 | Cons(i32, Box), -``` - -``` | ++++ + ``` @@ -398,81 +299,28 @@ Filename: src/main.rs ``` enum List { -``` - -``` Cons(i32, Box), -``` - -``` Nil, -``` - -``` } -``` -``` - -``` - -``` use crate::List::{Cons, Nil}; -``` -``` - -``` - -``` fn main() { -``` - -``` let list = Cons( -``` - -``` 1, -``` - -``` Box::new(Cons( -``` - -``` 2, -``` - -``` Box::new(Cons( -``` - -``` 3, -``` - -``` Box::new(Nil) -``` - -``` )) -``` - -``` )) -``` - -``` ); -``` - -``` } ``` -Definition of `List` that uses `Box` in order to have a known size +Listing 15-5: Definition of `List` that uses `Box` in order to have a known +size The `Cons` variant needs the size of an `i32` plus the space to store the box’s pointer data. The `Nil` variant stores no values, so it needs less space than @@ -482,13 +330,11 @@ the infinite, recursive chain, so the compiler can figure out the size it needs to store a `List` value. Figure 15-2 shows what the `Cons` variant looks like now. +Figure 15-2: A `List` that is not infinitely sized, because `Cons` holds a `Box` -Unmatched: GraphicSlug - -Unmatched: CaptionLine - Boxes provide only the indirection and heap allocation; they don’t have -any other special capabilities, like those we’ll see with the other smart -pointer types. They also don’t have the performance overhead that these special +Boxes provide only the indirection and heap allocation; they don’t have any +other special capabilities, like those we’ll see with the other smart pointer +types. They also don’t have the performance overhead that these special capabilities incur, so they can be useful in cases like the cons list where the indirection is the only feature we need. We’ll look at more use cases for boxes in Chapter 17. @@ -517,7 +363,7 @@ smart pointers to work in ways similar to references. Then we’ll look at Rust *deref coercion* feature and how it lets us work with either references or smart pointers. -> NoteThere’s one big difference between the `MyBox` type we’re about to +> Note: There’s one big difference between the `MyBox` type we’re about to build and the real `Box`: our version will not store its data on the heap. We are focusing this example on `Deref`, so where the data is actually stored is less important than the pointer-like behavior. @@ -533,33 +379,16 @@ Filename: src/main.rs ``` fn main() { -``` - -``` 1 let x = 5; -``` - -``` 2 let y = &x; -``` -``` - -``` - -``` 3 assert_eq!(5, x); -``` - -``` 4 assert_eq!(5, *y); -``` - -``` } ``` -Using the dereference operator to follow a reference to an `i32` value +Listing 15-6: Using the dereference operator to follow a reference to an `i32` +value The variable `x` holds an `i32` value `5` [1]. We set `y` equal to a reference to `x` [2]. We can assert that `x` is equal to `5` [3]. However, if we want to @@ -573,37 +402,13 @@ error: ``` error[E0277]: can't compare `{integer}` with `&{integer}` -``` - -``` --> src/main.rs:6:5 -``` - -``` | -``` - -``` 6 | assert_eq!(5, y); -``` - -``` | ^^^^^^^^^^^^^^^^ no implementation for `{integer} == -``` - -``` &{integer}` -``` - -``` | -``` - -``` = help: the trait `PartialEq<&{integer}>` is not implemented -``` - -``` for `{integer}` ``` @@ -622,33 +427,15 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let x = 5; -``` - -``` 1 let y = Box::new(x); -``` -``` - -``` - -``` assert_eq!(5, x); -``` - -``` 2 assert_eq!(5, *y); -``` - -``` } ``` -Using the dereference operator on a `Box` +Listing 15-7: Using the dereference operator on a `Box` The main difference between Listing 15-7 and Listing 15-6 is that here we set `y` to be an instance of a box pointing to a copied value of `x` rather than a @@ -673,33 +460,15 @@ Filename: src/main.rs ``` 1 struct MyBox(T); -``` -``` - -``` - -``` impl MyBox { -``` - -``` 2 fn new(x: T) -> MyBox { -``` - -``` 3 MyBox(x) -``` - -``` } -``` - -``` } ``` -Defining a `MyBox` type +Listing 15-8: Defining a `MyBox` type We define a struct named `MyBox` and declare a generic parameter `T` [1] because we want our type to hold values of any type. The `MyBox` type is a @@ -716,53 +485,24 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let x = 5; -``` - -``` let y = MyBox::new(x); -``` -``` - -``` - -``` assert_eq!(5, x); -``` - -``` assert_eq!(5, *y); -``` - -``` } ``` -Attempting to use `MyBox` in the same way we used references and `Box` +Listing 15-9: Attempting to use `MyBox` in the same way we used references +and `Box` Here’s the resultant compilation error: ``` error[E0614]: type `MyBox<{integer}>` cannot be dereferenced -``` - -``` --> src/main.rs:14:19 -``` - -``` | -``` - -``` 14 | assert_eq!(5, *y); -``` - -``` | ^^ ``` @@ -783,41 +523,17 @@ Filename: src/main.rs ``` use std::ops::Deref; -``` -``` - -``` - -``` impl Deref for MyBox { -``` - -``` 1 type Target = T; -``` -``` - -``` - -``` fn deref(&self) -> &Self::Target { -``` - -``` 2 &self.0 -``` - -``` } -``` - -``` } ``` -Implementing `Deref` on `MyBox` +Listing 15-10: Implementing `Deref` on `MyBox` The `type Target = T;` syntax [1] defines an associated type for the `Deref` trait to use. Associated types are a slightly different way of declaring a @@ -888,17 +604,11 @@ Filename: src/main.rs ``` fn hello(name: &str) { -``` - -``` println!("Hello, {name}!"); -``` - -``` } ``` -A `hello` function that has the parameter `name` of type `&str` +Listing 15-11: A `hello` function that has the parameter `name` of type `&str` We can call the `hello` function with a string slice as an argument, such as `hello("Rust");`, for example. Deref coercion makes it possible to call `hello` @@ -908,22 +618,13 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let m = MyBox::new(String::from("Rust")); -``` - -``` hello(&m); -``` - -``` } ``` -Calling `hello` with a reference to a `MyBox` value, which works -because of deref coercion +Listing 15-12: Calling `hello` with a reference to a `MyBox` value, +which works because of deref coercion Here we’re calling the `hello` function with the argument `&m`, which is a reference to a `MyBox` value. Because we implemented the `Deref` trait @@ -941,21 +642,13 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let m = MyBox::new(String::from("Rust")); -``` - -``` hello(&(*m)[..]); -``` - -``` } ``` -The code we would have to write if Rust didn’t have deref coercion +Listing 15-13: The code we would have to write if Rust didn’t have deref +coercion The `(*m)` dereferences the `MyBox` into a `String`. Then the `&` and `[..]` take a string slice of the `String` that is equal to the whole string to @@ -981,6 +674,7 @@ 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` + The first two cases are the same except that the second implements mutability. The first case states that if you have a `&T`, and `T` implements `Deref` to some type `U`, you can get a `&U` transparently. The second case states that @@ -1032,94 +726,31 @@ Filename: src/main.rs ``` struct CustomSmartPointer { -``` - -``` data: String, -``` - -``` } -``` -``` - -``` - -``` 1 impl Drop for CustomSmartPointer { -``` - -``` fn drop(&mut self) { -``` - -``` 2 println!( -``` - -``` "Dropping CustomSmartPointer with data `{}`!", -``` - -``` self.data -``` - -``` ); -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` fn main() { -``` - -``` 3 let c = CustomSmartPointer { -``` - -``` data: String::from("my stuff"), -``` - -``` }; -``` - -``` 4 let d = CustomSmartPointer { -``` - -``` data: String::from("other stuff"), -``` - -``` }; -``` - -``` 5 println!("CustomSmartPointers created."); -``` - -``` 6 } ``` -A `CustomSmartPointer` struct that implements the `Drop` trait where we would -put our cleanup code +Listing 15-14: A `CustomSmartPointer` struct that implements the `Drop` trait +where we would put our cleanup code The `Drop` trait is included in the prelude, so we don’t need to bring it into scope. We implement the `Drop` trait on `CustomSmartPointer` [1] and provide an @@ -1138,13 +769,7 @@ When we run this program, we’ll see the following output: ``` CustomSmartPointers created. -``` - -``` Dropping CustomSmartPointer with data `other stuff`! -``` - -``` Dropping CustomSmartPointer with data `my stuff`! ``` @@ -1173,78 +798,30 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let c = CustomSmartPointer { -``` - -``` data: String::from("some data"), -``` - -``` }; -``` - -``` println!("CustomSmartPointer created."); -``` - -``` c.drop(); -``` - -``` println!( -``` - -``` "CustomSmartPointer dropped before the end of main." -``` - -``` ); -``` - -``` } ``` -Attempting to call the `drop` method from the `Drop` trait manually to clean up -early +Listing 15-15: Attempting to call the `drop` method from the `Drop` trait +manually to clean up early When we try to compile this code, we’ll get this error: ``` error[E0040]: explicit use of destructor method -``` - -``` --> src/main.rs:16:7 -``` - -``` | -``` - -``` 16 | c.drop(); -``` - -``` | --^^^^-- -``` - -``` | | | -``` - -``` | | explicit destructor calls not allowed -``` - -``` | help: consider using `drop` function: `drop(c)` ``` @@ -1272,57 +849,25 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let c = CustomSmartPointer { -``` - -``` data: String::from("some data"), -``` - -``` }; -``` - -``` println!("CustomSmartPointer created."); -``` - -``` drop(c); -``` - -``` println!( -``` - -``` "CustomSmartPointer dropped before the end of main." -``` - -``` ); -``` - -``` } ``` -Calling `std::mem::drop` to explicitly drop a value before it goes out of scope +Listing 15-16: Calling `std::mem::drop` to explicitly drop a value before it +goes out of scope Running this code will print the following: ``` CustomSmartPointer created. -``` - -``` Dropping CustomSmartPointer with data `some data`! -``` - -``` CustomSmartPointer dropped before the end of main. ``` @@ -1382,15 +927,12 @@ Let’s return to our cons list example in Listing 15-5. Recall that we defined it using `Box`. This time, we’ll create two lists that both share ownership of a third list. Conceptually, this looks similar to Figure 15-3. +Figure 15-3: Two lists, `b` and `c`, sharing ownership of a third list, `a` -Unmatched: GraphicSlug - -Unmatched: CaptionLine - We’ll create list `a` that contains `5` and then `10`. Then we’ll make -two more lists: `b` that starts with `3` and `c` that starts with `4`. Both `b` -and `c` lists will then continue on to the first `a` list containing `5` and -`10`. In other words, both lists will share the first list containing `5` and -`10`. +We’ll create list `a` that contains `5` and then `10`. Then we’ll make two more +lists: `b` that starts with `3` and `c` that starts with `4`. Both `b` and `c` +lists will then continue on to the first `a` list containing `5` and `10`. In +other words, both lists will share the first list containing `5` and `10`. Trying to implement this scenario using our definition of `List` with `Box` won’t work, as shown in Listing 15-17. @@ -1399,94 +941,34 @@ Filename: src/main.rs ``` enum List { -``` - -``` Cons(i32, Box), -``` - -``` Nil, -``` - -``` } -``` -``` - -``` - -``` use crate::List::{Cons, Nil}; -``` -``` - -``` - -``` fn main() { -``` - -``` let a = Cons(5, Box::new(Cons(10, Box::new(Nil)))); -``` - -``` 1 let b = Cons(3, Box::new(a)); -``` - -``` 2 let c = Cons(4, Box::new(a)); -``` - -``` } ``` -Demonstrating that we’re not allowed to have two lists using `Box` that try -to share ownership of a third list +Listing 15-17: Demonstrating that we’re not allowed to have two lists using +`Box` that try to share ownership of a third list When we compile this code, we get this error: ``` error[E0382]: use of moved value: `a` -``` - -``` --> src/main.rs:11:30 -``` - -``` | -``` - -``` 9 | let a = Cons(5, Box::new(Cons(10, Box::new(Nil)))); -``` - -``` | - move occurs because `a` has type `List`, which -``` - -``` does not implement the `Copy` trait -``` - -``` 10 | let b = Cons(3, Box::new(a)); -``` - -``` | - value moved here -``` - -``` 11 | let c = Cons(4, Box::new(a)); -``` - -``` | ^ value used here after move ``` @@ -1515,57 +997,21 @@ Filename: src/main.rs ``` enum List { -``` - -``` Cons(i32, Rc), -``` - -``` Nil, -``` - -``` } -``` -``` - -``` - -``` use crate::List::{Cons, Nil}; -``` - -``` 1 use std::rc::Rc; -``` -``` - -``` - -``` fn main() { -``` - -``` 2 let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil))))); -``` - -``` 3 let b = Cons(3, Rc::clone(&a)); -``` - -``` 4 let c = Cons(4, Rc::clone(&a)); -``` - -``` } ``` -A definition of `List` that uses `Rc` +Listing 15-18: A definition of `List` that uses `Rc` We need to add a `use` statement to bring `Rc` into scope [1] because it’s not in the prelude. In `main`, we create the list holding `5` and `10` and @@ -1596,105 +1042,33 @@ Filename: src/main.rs ``` --snip-- -``` -``` - -``` - -``` fn main() { -``` - -``` let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil))))); -``` - -``` println!( -``` - -``` "count after creating a = {}", -``` - -``` Rc::strong_count(&a) -``` - -``` ); -``` - -``` let b = Cons(3, Rc::clone(&a)); -``` - -``` println!( -``` - -``` "count after creating b = {}", -``` - -``` Rc::strong_count(&a) -``` - -``` ); -``` - -``` { -``` - -``` let c = Cons(4, Rc::clone(&a)); -``` - -``` println!( -``` - -``` "count after creating c = {}", -``` - -``` Rc::strong_count(&a) -``` - -``` ); -``` - -``` } -``` - -``` println!( -``` - -``` "count after c goes out of scope = {}", -``` - -``` Rc::strong_count(&a) -``` - -``` ); -``` - -``` } ``` -Printing the reference count +Listing 15-19: Printing the reference count 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 @@ -1706,17 +1080,8 @@ This code prints the following: ``` count after creating a = 1 -``` - -``` count after creating b = 2 -``` - -``` count after creating c = 3 -``` - -``` count after c goes out of scope = 2 ``` @@ -1769,6 +1134,7 @@ Recall the borrowing rules you learned in Chapter 4: * At any given time, you can have *either* one mutable reference or any number of immutable references (but not both). * References must always be valid. + With references and `Box`, the borrowing rules’ invariants are enforced at compile time. With `RefCell`, these invariants are enforced *at runtime*. With references, if you break these rules, you’ll get a compiler error. With @@ -1811,6 +1177,7 @@ immutable or mutable borrows checked at runtime. * Because `RefCell` allows mutable borrows checked at runtime, you can mutate the value inside the `RefCell` even when the `RefCell` is immutable. + Mutating the value inside an immutable value is the *interior mutability* pattern. Let’s look at a situation in which interior mutability is useful and examine how it’s possible. @@ -1824,17 +1191,8 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let x = 5; -``` - -``` let y = &mut x; -``` - -``` } ``` @@ -1842,33 +1200,12 @@ If you tried to compile this code, you’d get the following error: ``` error[E0596]: cannot borrow `x` as mutable, as it is not declared -``` - -``` as mutable -``` - -``` --> src/main.rs:3:13 -``` - -``` | -``` - -``` 2 | let x = 5; -``` - -``` | - help: consider changing this to be mutable: `mut x` -``` - -``` 3 | let y = &mut x; -``` - -``` | ^^^^^^ cannot borrow as mutable ``` @@ -1917,178 +1254,52 @@ Filename: src/lib.rs ``` pub trait Messenger { -``` - -``` 1 fn send(&self, msg: &str); -``` - -``` } -``` -``` - -``` - -``` pub struct LimitTracker<'a, T: Messenger> { -``` - -``` messenger: &'a T, -``` - -``` value: usize, -``` - -``` max: usize, -``` - -``` } -``` -``` - -``` - -``` impl<'a, T> LimitTracker<'a, T> -``` - -``` where -``` - -``` T: Messenger, -``` - -``` { -``` - -``` pub fn new( -``` - -``` messenger: &'a T, -``` - -``` max: usize -``` - -``` ) -> LimitTracker<'a, T> { -``` - -``` LimitTracker { -``` - -``` messenger, -``` - -``` value: 0, -``` - -``` max, -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` 2 pub fn set_value(&mut self, value: usize) { -``` - -``` self.value = value; -``` -``` - -``` - -``` let percentage_of_max = -``` - -``` self.value as f64 / self.max as f64; -``` -``` - -``` - -``` if percentage_of_max >= 1.0 { -``` - -``` self.messenger -``` - -``` .send("Error: You are over your quota!"); -``` - -``` } else if percentage_of_max >= 0.9 { -``` - -``` self.messenger -``` - -``` .send("Urgent: You're at 90% of your quota!"); -``` - -``` } else if percentage_of_max >= 0.75 { -``` - -``` self.messenger -``` - -``` .send("Warning: You're at 75% of your quota!"); -``` - -``` } -``` - -``` } -``` - -``` } ``` -A library to keep track of how close a value is to a maximum value and warn -when the value is at certain levels +Listing 15-20: A library to keep track of how close a value is to a maximum +value and warn when the value is at certain levels One important part of this code is that the `Messenger` trait has one method called `send` that takes an immutable reference to `self` and the text of the @@ -2113,146 +1324,44 @@ Filename: src/lib.rs ``` #[cfg(test)] -``` - -``` mod tests { -``` - -``` use super::*; -``` -``` - -``` - -``` 1 struct MockMessenger { -``` - -``` 2 sent_messages: Vec, -``` - -``` } -``` -``` - -``` - -``` impl MockMessenger { -``` - -``` 3 fn new() -> MockMessenger { -``` - -``` MockMessenger { -``` - -``` sent_messages: vec![], -``` - -``` } -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` 4 impl Messenger for MockMessenger { -``` - -``` fn send(&self, message: &str) { -``` - -``` 5 self.sent_messages.push(String::from(message)); -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` #[test] -``` - -``` 6 fn it_sends_an_over_75_percent_warning_message() { -``` - -``` let mock_messenger = MockMessenger::new(); -``` - -``` let mut limit_tracker = LimitTracker::new( -``` - -``` &mock_messenger, -``` - -``` 100 -``` - -``` ); -``` -``` - -``` - -``` limit_tracker.set_value(80); -``` -``` - -``` - -``` assert_eq!(mock_messenger.sent_messages.len(), 1); -``` - -``` } -``` - -``` } ``` -An attempt to implement a `MockMessenger` that isn’t allowed by the borrow -checker +Listing 15-21: An attempt to implement a `MockMessenger` that isn’t allowed by +the borrow checker This test code defines a `MockMessenger` struct [1] that has a `sent_messages` field with a `Vec` of `String` values [2] to keep track of the messages it’s @@ -2276,45 +1385,15 @@ However, there’s one problem with this test, as shown here: ``` error[E0596]: cannot borrow `self.sent_messages` as mutable, as it is behind a -``` - -``` `&` reference -``` - -``` --> src/lib.rs:58:13 -``` - -``` | -``` - -``` 2 | fn send(&self, msg: &str); -``` - -``` | ----- help: consider changing that to be a mutable reference: -``` - -``` `&mut self` -``` - -``` ... -``` - -``` 58 | self.sent_messages.push(String::from(message)); -``` - -``` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self` is a -``` - -``` `&` reference, so the data it refers to cannot be borrowed as mutable ``` @@ -2333,146 +1412,44 @@ Filename: src/lib.rs ``` #[cfg(test)] -``` - -``` mod tests { -``` - -``` use super::*; -``` - -``` use std::cell::RefCell; -``` -``` - -``` - -``` struct MockMessenger { -``` - -``` 1 sent_messages: RefCell>, -``` - -``` } -``` -``` - -``` - -``` impl MockMessenger { -``` - -``` fn new() -> MockMessenger { -``` - -``` MockMessenger { -``` - -``` 2 sent_messages: RefCell::new(vec![]), -``` - -``` } -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` impl Messenger for MockMessenger { -``` - -``` fn send(&self, message: &str) { -``` - -``` self.sent_messages -``` - -``` 3 .borrow_mut() -``` - -``` .push(String::from(message)); -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` #[test] -``` - -``` fn it_sends_an_over_75_percent_warning_message() { -``` - -``` --snip-- -``` -``` - -``` - -``` assert_eq!( -``` - -``` 4 mock_messenger.sent_messages.borrow().len(), -``` - -``` 1 -``` - -``` ); -``` - -``` } -``` - -``` } ``` -Using `RefCell` to mutate an inner value while the outer value is considered -immutable +Listing 15-22: Using `RefCell` to mutate an inner value while the outer +value is considered immutable The `sent_messages` field is now of type `RefCell>` [1] instead of `Vec`. In the `new` function, we create a new `RefCell>` @@ -2518,42 +1495,18 @@ Filename: src/lib.rs ``` impl Messenger for MockMessenger { -``` - -``` fn send(&self, message: &str) { -``` - -``` let mut one_borrow = self.sent_messages.borrow_mut(); -``` - -``` let mut two_borrow = self.sent_messages.borrow_mut(); -``` -``` - -``` - -``` one_borrow.push(String::from(message)); -``` - -``` two_borrow.push(String::from(message)); -``` - -``` } -``` - -``` } ``` -Creating two mutable references in the same scope to see that `RefCell` will -panic +Listing 15-23: Creating two mutable references in the same scope to see that +`RefCell` will panic We create a variable `one_borrow` for the `RefMut` smart pointer returned from `borrow_mut`. Then we create another mutable borrow in the same way in the @@ -2563,13 +1516,7 @@ which isn’t allowed. When we run the tests for our library, the code in Listin ``` ---- tests::it_sends_an_over_75_percent_warning_message stdout ---- -``` - -``` thread 'main' panicked at 'already borrowed: BorrowMutError', src/lib.rs:60:53 -``` - -``` note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ``` @@ -2607,101 +1554,32 @@ Filename: src/main.rs ``` #[derive(Debug)] -``` - -``` enum List { -``` - -``` Cons(Rc>, Rc), -``` - -``` Nil, -``` - -``` } -``` -``` - -``` - -``` use crate::List::{Cons, Nil}; -``` - -``` use std::cell::RefCell; -``` - -``` use std::rc::Rc; -``` -``` - -``` - -``` fn main() { -``` - -``` 1 let value = Rc::new(RefCell::new(5)); -``` -``` - -``` - -``` 2 let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil))); -``` -``` - -``` - -``` let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a)); -``` - -``` let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a)); -``` -``` - -``` - -``` 3 *value.borrow_mut() += 10; -``` -``` - -``` - -``` println!("a after = {:?}", a); -``` - -``` println!("b after = {:?}", b); -``` - -``` println!("c after = {:?}", c); -``` - -``` } ``` -Using `Rc>` to create a `List` that we can mutate +Listing 15-24: Using `Rc>` to create a `List` that we can mutate We create a value that is an instance of `Rc>` and store it in a variable named `value` [1] so we can access it directly later. Then we create a @@ -2725,13 +1603,7 @@ value of `15` rather than `5`: ``` a after = Cons(RefCell { value: 15 }, Nil) -``` - -``` b after = Cons(RefCell { value: 3 }, Cons(RefCell { value: 15 }, Nil)) -``` - -``` c after = Cons(RefCell { value: 4 }, Cons(RefCell { value: 15 }, Nil)) ``` @@ -2765,78 +1637,27 @@ Filename: src/main.rs ``` use crate::List::{Cons, Nil}; -``` - -``` use std::cell::RefCell; -``` - -``` use std::rc::Rc; -``` -``` - -``` - -``` #[derive(Debug)] -``` - -``` enum List { -``` - -``` 1 Cons(i32, RefCell>), -``` - -``` Nil, -``` - -``` } -``` -``` - -``` - -``` impl List { -``` - -``` 2 fn tail(&self) -> Option<&RefCell>> { -``` - -``` match self { -``` - -``` Cons(_, item) => Some(item), -``` - -``` Nil => None, -``` - -``` } -``` - -``` } -``` - -``` } ``` -A cons list definition that holds a `RefCell` so we can modify what a `Cons` -variant is referring to +Listing 15-25: A cons list definition that holds a `RefCell` so we can +modify what a `Cons` variant is referring to We’re using another variation of the `List` definition from Listing 15-5. The second element in the `Cons` variant is now `RefCell>` [1], meaning @@ -2855,133 +1676,41 @@ Filename: src/main.rs ``` fn main() { -``` - -``` 1 let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil)))); -``` -``` - -``` - -``` println!("a initial rc count = {}", Rc::strong_count(&a)); -``` - -``` println!("a next item = {:?}", a.tail()); -``` -``` - -``` - -``` 2 let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a)))); -``` -``` - -``` - -``` println!( -``` - -``` "a rc count after b creation = {}", -``` - -``` Rc::strong_count(&a) -``` - -``` ); -``` - -``` println!("b initial rc count = {}", Rc::strong_count(&b)); -``` - -``` println!("b next item = {:?}", b.tail()); -``` -``` - -``` - -``` 3 if let Some(link) = a.tail() { -``` - -``` 4 *link.borrow_mut() = Rc::clone(&b); -``` - -``` } -``` -``` - -``` - -``` println!( -``` - -``` "b rc count after changing a = {}", -``` - -``` Rc::strong_count(&b) -``` - -``` ); -``` - -``` 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 -``` - -``` // println!("a next item = {:?}", a.tail()); -``` - -``` } ``` -Creating a reference cycle of two `List` values pointing to each other +Listing 15-26: Creating a reference cycle of two `List` values pointing to each +other We create an `Rc` instance holding a `List` value in the variable `a` with an initial list of `5, Nil` [1]. We then create an `Rc` instance @@ -2999,29 +1728,11 @@ moment, we’ll get this output: ``` a initial rc count = 1 -``` - -``` a next item = Some(RefCell { value: Nil }) -``` - -``` a rc count after b creation = 2 -``` - -``` b initial rc count = 1 -``` - -``` b next item = Some(RefCell { value: Cons(5, RefCell { value: Nil }) }) -``` - -``` b rc count after changing a = 2 -``` - -``` a rc count after changing a = 2 ``` @@ -3036,13 +1747,11 @@ to 1 as well. This instance’s memory can’t be dropped either, because the ot remain uncollected forever. To visualize this reference cycle, we’ve created a diagram in Figure 15-4. +Figure 15-4: A reference cycle of lists `a` and `b` pointing to each other -Unmatched: GraphicSlug - -Unmatched: CaptionLine - 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. +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. 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 @@ -3111,33 +1820,12 @@ Filename: src/main.rs ``` use std::cell::RefCell; -``` - -``` use std::rc::Rc; -``` -``` - -``` - -``` #[derive(Debug)] -``` - -``` struct Node { -``` - -``` value: i32, -``` - -``` children: RefCell>>, -``` - -``` } ``` @@ -3155,50 +1843,20 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let leaf = Rc::new(Node { -``` - -``` value: 3, -``` - -``` children: RefCell::new(vec![]), -``` - -``` }); -``` -``` - -``` - -``` let branch = Rc::new(Node { -``` - -``` value: 5, -``` - -``` children: RefCell::new(vec![Rc::clone(&leaf)]), -``` - -``` }); -``` - -``` } ``` -Creating a `leaf` node with no children and a `branch` node with `leaf` as one -of its children +Listing 15-27: Creating a `leaf` node with no children and a `branch` node with +`leaf` as one of its children We clone the `Rc` in `leaf` and store that in `branch`, meaning the `Node` in `leaf` now has two owners: `leaf` and `branch`. We can get from @@ -3229,37 +1887,13 @@ Filename: src/main.rs ``` use std::cell::RefCell; -``` - -``` use std::rc::{Rc, Weak}; -``` -``` - -``` - -``` #[derive(Debug)] -``` - -``` struct Node { -``` - -``` value: i32, -``` - -``` parent: RefCell>, -``` - -``` children: RefCell>>, -``` - -``` } ``` @@ -3271,105 +1905,33 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let leaf = Rc::new(Node { -``` - -``` value: 3, -``` - -``` 1 parent: RefCell::new(Weak::new()), -``` - -``` children: RefCell::new(vec![]), -``` - -``` }); -``` -``` - -``` - -``` 2 println!( -``` - -``` "leaf parent = {:?}", -``` - -``` leaf.parent.borrow().upgrade() -``` - -``` ); -``` -``` - -``` - -``` let branch = Rc::new(Node { -``` - -``` value: 5, -``` - -``` 3 parent: RefCell::new(Weak::new()), -``` - -``` children: RefCell::new(vec![Rc::clone(&leaf)]), -``` - -``` }); -``` -``` - -``` - -``` 4 *leaf.parent.borrow_mut() = Rc::downgrade(&branch); -``` -``` - -``` - -``` 5 println!( -``` - -``` "leaf parent = {:?}", -``` - -``` leaf.parent.borrow().upgrade() -``` - -``` ); -``` - -``` } ``` -A `leaf` node with a weak reference to its parent node, `branch` +Listing 15-28: A `leaf` node with a weak reference to its parent node, `branch` Creating the `leaf` node looks similar to Listing 15-27 with the exception of the `parent` field: `leaf` starts out without a parent, so we create a new, @@ -3399,13 +1961,7 @@ we had in Listing 15-26; the `Weak` references are printed as `(Weak)`: ``` leaf parent = Some(Node { value: 5, parent: RefCell { value: (Weak) }, -``` - -``` children: RefCell { value: [Node { value: 3, parent: RefCell { value: (Weak) }, -``` - -``` children: RefCell { value: [] } }] } }) ``` @@ -3425,186 +1981,54 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let leaf = Rc::new(Node { -``` - -``` value: 3, -``` - -``` parent: RefCell::new(Weak::new()), -``` - -``` children: RefCell::new(vec![]), -``` - -``` }); -``` -``` - -``` - -``` 1 println!( -``` - -``` "leaf strong = {}, weak = {}", -``` - -``` Rc::strong_count(&leaf), -``` - -``` Rc::weak_count(&leaf), -``` - -``` ); -``` -``` - -``` - -``` 2 { -``` - -``` let branch = Rc::new(Node { -``` - -``` value: 5, -``` - -``` parent: RefCell::new(Weak::new()), -``` - -``` children: RefCell::new(vec![Rc::clone(&leaf)]), -``` - -``` }); -``` -``` - -``` - -``` *leaf.parent.borrow_mut() = Rc::downgrade(&branch); -``` -``` - -``` - -``` 3 println!( -``` - -``` "branch strong = {}, weak = {}", -``` - -``` Rc::strong_count(&branch), -``` - -``` Rc::weak_count(&branch), -``` - -``` ); -``` -``` - -``` - -``` 4 println!( -``` - -``` "leaf strong = {}, weak = {}", -``` - -``` Rc::strong_count(&leaf), -``` - -``` Rc::weak_count(&leaf), -``` - -``` ); -``` - -``` 5 } -``` -``` - -``` - -``` 6 println!( -``` - -``` "leaf parent = {:?}", -``` - -``` leaf.parent.borrow().upgrade() -``` - -``` ); -``` - -``` 7 println!( -``` - -``` "leaf strong = {}, weak = {}", -``` - -``` Rc::strong_count(&leaf), -``` - -``` Rc::weak_count(&leaf), -``` - -``` ); -``` - -``` } ``` -Creating `branch` in an inner scope and examining strong and weak reference -counts +Listing 15-29: Creating `branch` in an inner scope and examining strong and +weak reference counts After `leaf` is created, its `Rc` has a strong count of 1 and a weak count of 0 [1]. In the inner scope [2], we create `branch` and associate it diff --git a/nostarch/chapter16.md b/nostarch/chapter16.md index 20fe0396..7595fa69 100644 --- a/nostarch/chapter16.md +++ b/nostarch/chapter16.md @@ -30,11 +30,11 @@ shipped to production. We’ve nicknamed this aspect of Rust *fearless* *concurrency*. Fearless concurrency allows you to write code that is free of subtle bugs and is easy to refactor without introducing new bugs. -> NoteFor simplicity’s sake, we’ll refer to many of the problems as -*concurrent* rather than being more precise by saying *concurrent and/or* -*parallel*. If this book were about concurrency and/or parallelism, we’d be -more specific. For this chapter, please mentally substitute *concurrent* -*and/or parallel* whenever we use *concurrent*. +> Note: For simplicity’s sake, we’ll refer to many of the problems as +*concurrent* rather than being more precise by saying *concurrent and/or +parallel*. If this book were about concurrency and/or parallelism, we’d be more +specific. For this chapter, please mentally substitute *concurrent and/or +parallel* whenever we use *concurrent*. Many languages are dogmatic about the solutions they offer for handling concurrent problems. For example, Erlang has elegant functionality for @@ -55,6 +55,7 @@ Here are the topics we’ll cover in this chapter: of data * The `Sync` and `Send` traits, which extend Rust’s concurrency guarantees to user-defined types as well as types provided by the standard library + ## Using Threads to Run Code Simultaneously In most current operating systems, an executed program’s code is run in a @@ -76,6 +77,7 @@ inconsistent order threads from continuing * Bugs that happen only in certain situations and are hard to reproduce and fix reliably + Rust attempts to mitigate the negative effects of using threads, but programming in a multithreaded context still takes careful thought and requires a code structure that is different from that in programs running in a single @@ -99,70 +101,25 @@ Filename: src/main.rs ``` use std::thread; -``` - -``` use std::time::Duration; -``` -``` - -``` - -``` fn main() { -``` - -``` thread::spawn(|| { -``` - -``` for i in 1..10 { -``` - -``` println!("hi number {i} from the spawned thread!"); -``` - -``` thread::sleep(Duration::from_millis(1)); -``` - -``` } -``` - -``` }); -``` -``` - -``` - -``` for i in 1..5 { -``` - -``` println!("hi number {i} from the main thread!"); -``` - -``` thread::sleep(Duration::from_millis(1)); -``` - -``` } -``` - -``` } ``` -Creating a new thread to print one thing while the main thread prints something -else +Listing 16-1: Creating a new thread to print one thing while the main thread +prints something else Note that when the main thread of a Rust program completes, all spawned threads are shut down, whether or not they have finished running. The output from this @@ -171,37 +128,13 @@ following: ``` hi number 1 from the main thread! -``` - -``` hi number 1 from the spawned thread! -``` - -``` hi number 2 from the main thread! -``` - -``` hi number 2 from the spawned thread! -``` - -``` hi number 3 from the main thread! -``` - -``` hi number 3 from the spawned thread! -``` - -``` hi number 4 from the main thread! -``` - -``` hi number 4 from the spawned thread! -``` - -``` hi number 5 from the spawned thread! ``` @@ -226,9 +159,9 @@ will get to run at all! We can fix the problem of the spawned thread not running or of it ending prematurely by saving the return value of `thread::spawn` in a variable. The -return type of `thread::spawn` is `JoinHandle```. A `JoinHandle``` is an +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 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. @@ -236,78 +169,27 @@ Filename: src/main.rs ``` use std::thread; -``` - -``` use std::time::Duration; -``` -``` - -``` - -``` fn main() { -``` - -``` let handle = thread::spawn(|| { -``` - -``` for i in 1..10 { -``` - -``` println!("hi number {i} from the spawned thread!"); -``` - -``` thread::sleep(Duration::from_millis(1)); -``` - -``` } -``` - -``` }); -``` -``` - -``` - -``` for i in 1..5 { -``` - -``` println!("hi number {i} from the main thread!"); -``` - -``` thread::sleep(Duration::from_millis(1)); -``` - -``` } -``` -``` - -``` - -``` handle.join().unwrap(); -``` - -``` } ``` -Saving a `JoinHandle``` from `thread::spawn` to guarantee the thread is run -to completion +Listing 16-2: Saving a `JoinHandle` from `thread::spawn` to guarantee the +thread is run to completion Calling `join` on the handle blocks the thread currently running until the thread represented by the handle terminates. *Blocking* a thread means that @@ -317,53 +199,17 @@ produce output similar to this: ``` hi number 1 from the main thread! -``` - -``` hi number 2 from the main thread! -``` - -``` hi number 1 from the spawned thread! -``` - -``` hi number 3 from the main thread! -``` - -``` hi number 2 from the spawned thread! -``` - -``` hi number 4 from the main thread! -``` - -``` hi number 3 from the spawned thread! -``` - -``` hi number 4 from the spawned thread! -``` - -``` hi number 5 from the spawned thread! -``` - -``` hi number 6 from the spawned thread! -``` - -``` hi number 7 from the spawned thread! -``` - -``` hi number 8 from the spawned thread! -``` - -``` hi number 9 from the spawned thread! ``` @@ -377,73 +223,22 @@ Filename: src/main.rs ``` use std::thread; -``` - -``` use std::time::Duration; -``` -``` - -``` - -``` fn main() { -``` - -``` let handle = thread::spawn(|| { -``` - -``` for i in 1..10 { -``` - -``` println!("hi number {i} from the spawned thread!"); -``` - -``` thread::sleep(Duration::from_millis(1)); -``` - -``` } -``` - -``` }); -``` -``` - -``` - -``` handle.join().unwrap(); -``` -``` - -``` - -``` for i in 1..5 { -``` - -``` println!("hi number {i} from the main thread!"); -``` - -``` thread::sleep(Duration::from_millis(1)); -``` - -``` } -``` - -``` } ``` @@ -452,53 +247,17 @@ The main thread will wait for the spawned thread to finish and then run its ``` hi number 1 from the spawned thread! -``` - -``` hi number 2 from the spawned thread! -``` - -``` hi number 3 from the spawned thread! -``` - -``` hi number 4 from the spawned thread! -``` - -``` hi number 5 from the spawned thread! -``` - -``` hi number 6 from the spawned thread! -``` - -``` hi number 7 from the spawned thread! -``` - -``` hi number 8 from the spawned thread! -``` - -``` hi number 9 from the spawned thread! -``` - -``` hi number 1 from the main thread! -``` - -``` hi number 2 from the main thread! -``` - -``` hi number 3 from the main thread! -``` - -``` hi number 4 from the main thread! ``` @@ -525,49 +284,20 @@ Filename: src/main.rs ``` use std::thread; -``` -``` - -``` - -``` fn main() { -``` - -``` let v = vec![1, 2, 3]; -``` -``` - -``` - -``` let handle = thread::spawn(|| { -``` - -``` println!("Here's a vector: {:?}", v); -``` - -``` }); -``` -``` - -``` - -``` handle.join().unwrap(); -``` - -``` } ``` -Attempting to use a vector created by the main thread in another thread +Listing 16-3: Attempting to use a vector created by the main thread in another +thread The closure uses `v`, so it will capture `v` and make it part of the closure’s environment. Because `thread::spawn` runs this closure in a new thread, we @@ -576,89 +306,26 @@ example, we get the following error: ``` error[E0373]: closure may outlive the current function, but it borrows `v`, -``` - -``` which is owned by the current function -``` - -``` --> src/main.rs:6:32 -``` - -``` | -``` - -``` 6 | let handle = thread::spawn(|| { -``` - -``` | ^^ may outlive borrowed value `v` -``` - -``` 7 | println!("Here's a vector: {:?}", v); -``` - -``` | - `v` is borrowed here -``` - -``` | -``` - -``` note: function requires argument type to outlive `'static` -``` - -``` --> src/main.rs:6:18 -``` - -``` | -``` - -``` 6 | let handle = thread::spawn(|| { -``` - -``` | __________________^ -``` - -``` 7 | | println!("Here's a vector: {:?}", v); -``` - -``` 8 | | }); -``` - -``` | |______^ -``` - -``` help: to force the closure to take ownership of `v` (and any other referenced -``` - -``` variables), use the `move` keyword -``` - -``` | -``` - -``` 6 | let handle = thread::spawn(move || { -``` - -``` | ++++ ``` @@ -674,58 +341,22 @@ Filename: src/main.rs ``` use std::thread; -``` -``` - -``` - -``` fn main() { -``` - -``` let v = vec![1, 2, 3]; -``` -``` - -``` - -``` let handle = thread::spawn(|| { -``` - -``` println!("Here's a vector: {:?}", v); -``` - -``` }); -``` -``` - -``` - -``` drop(v); // oh no! -``` -``` - -``` - -``` handle.join().unwrap(); -``` - -``` } ``` -A thread with a closure that attempts to capture a reference to `v` from a main -thread that drops `v` +Listing 16-4: A thread with a closure that attempts to capture a reference to +`v` from a main thread that drops `v` If Rust allowed us to run this code, there’s a possibility that the spawned thread would be immediately put in the background without running at all. The @@ -739,21 +370,9 @@ advice: ``` help: to force the closure to take ownership of `v` (and any other referenced -``` - -``` variables), use the `move` keyword -``` - -``` | -``` - -``` 6 | let handle = thread::spawn(move || { -``` - -``` | ++++ ``` @@ -766,50 +385,20 @@ Filename: src/main.rs ``` use std::thread; -``` -``` - -``` - -``` fn main() { -``` - -``` let v = vec![1, 2, 3]; -``` -``` - -``` - -``` let handle = thread::spawn(move || { -``` - -``` println!("Here's a vector: {:?}", v); -``` - -``` }); -``` -``` - -``` - -``` handle.join().unwrap(); -``` - -``` } ``` -Using the `move` keyword to force a closure to take ownership of the values it -uses +Listing 16-5: Using the `move` keyword to force a closure to take ownership of +the values it uses We might be tempted to try the same thing to fix the code in Listing 16-4 where the main thread called `drop` by using a `move` closure. However, this fix will @@ -820,61 +409,19 @@ thread. We would get this compiler error instead: ``` error[E0382]: use of moved value: `v` -``` - -``` --> src/main.rs:10:10 -``` - -``` | -``` - -``` 4 | let v = vec![1, 2, 3]; -``` - -``` | - move occurs because `v` has type `Vec`, which does not -``` - -``` implement the `Copy` trait -``` - -``` 5 | -``` - -``` 6 | let handle = thread::spawn(move || { -``` - -``` | ------- value moved into closure here -``` - -``` 7 | println!("Here's a vector: {:?}", v); -``` - -``` | - variable moved due to use in -``` - -``` closure -``` - -``` ... -``` - -``` 10 | drop(v); // oh no! -``` - -``` | ^ value used here after move ``` @@ -893,8 +440,8 @@ API, let’s look at some situations in which we can use threads. ## Using Message Passing to Transfer Data Between Threads -One increasingly popular approach to ensuring safe concurrency is *message* -*passing*, where threads or actors communicate by sending each other messages +One increasingly popular approach to ensuring safe concurrency is *message +passing*, where threads or actors communicate by sending each other messages containing data. Here’s the idea in a slogan from the Go language documentation at *https://golang.org/doc/effective_go.html#concurrency*: “Do not communicate by sharing memory; instead, share memory by communicating.” @@ -930,25 +477,13 @@ Filename: src/main.rs ``` use std::sync::mpsc; -``` -``` - -``` - -``` fn main() { -``` - -``` let (tx, rx) = mpsc::channel(); -``` - -``` } ``` -Creating a channel and assigning the two halves to `tx` and `rx` +Listing 16-6: Creating a channel and assigning the two halves to `tx` and `rx` We create a new channel using the `mpsc::channel` function; `mpsc` stands for *multiple producer, single consumer*. In short, the way Rust’s standard library @@ -978,49 +513,19 @@ Filename: src/main.rs ``` use std::sync::mpsc; -``` - -``` use std::thread; -``` -``` - -``` - -``` fn main() { -``` - -``` let (tx, rx) = mpsc::channel(); -``` -``` - -``` - -``` thread::spawn(move || { -``` - -``` let val = String::from("hi"); -``` - -``` tx.send(val).unwrap(); -``` - -``` }); -``` - -``` } ``` -Moving `tx` to a spawned thread and sending `"hi"` +Listing 16-7: Moving `tx` to a spawned thread and sending `"hi"` 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 @@ -1042,61 +547,22 @@ Filename: src/main.rs ``` use std::sync::mpsc; -``` - -``` use std::thread; -``` -``` - -``` - -``` fn main() { -``` - -``` let (tx, rx) = mpsc::channel(); -``` -``` - -``` - -``` thread::spawn(move || { -``` - -``` let val = String::from("hi"); -``` - -``` tx.send(val).unwrap(); -``` - -``` }); -``` -``` - -``` - -``` let received = rx.recv().unwrap(); -``` - -``` println!("Got: {received}"); -``` - -``` } ``` -Receiving the value `"``hi``"` in the main thread and printing it +Listing 16-8: Receiving the value `"hi"` in the main thread and printing it The receiver has two useful methods: `recv` and `try_recv`. We’re using `recv`, short for *receive*, which will block the main thread’s execution and wait @@ -1139,65 +605,23 @@ Filename: src/main.rs ``` use std::sync::mpsc; -``` - -``` use std::thread; -``` -``` - -``` - -``` fn main() { -``` - -``` let (tx, rx) = mpsc::channel(); -``` -``` - -``` - -``` thread::spawn(move || { -``` - -``` let val = String::from("hi"); -``` - -``` tx.send(val).unwrap(); -``` - -``` println!("val is {val}"); -``` - -``` }); -``` -``` - -``` - -``` let received = rx.recv().unwrap(); -``` - -``` println!("Got: {received}"); -``` - -``` } ``` -Attempting to use `val` after we’ve sent it down the channel +Listing 16-9: Attempting to use `val` after we’ve sent it down the channel Here, we try to print `val` after we’ve sent it down the channel via `tx.send`. Allowing this would be a bad idea: once the value has been sent to another @@ -1208,41 +632,14 @@ us an error if we try to compile the code in Listing 16-9: ``` error[E0382]: borrow of moved value: `val` -``` - -``` --> src/main.rs:10:31 -``` - -``` | -``` - -``` 8 | let val = String::from("hi"); -``` - -``` | --- move occurs because `val` has type `String`, which does -``` - -``` not implement the `Copy` trait -``` - -``` 9 | tx.send(val).unwrap(); -``` - -``` | --- value moved here -``` - -``` 10 | println!("val is {val}"); -``` - -``` | ^^^ value borrowed here after move ``` @@ -1263,105 +660,33 @@ Filename: src/main.rs ``` use std::sync::mpsc; -``` - -``` use std::thread; -``` - -``` use std::time::Duration; -``` -``` - -``` - -``` fn main() { -``` - -``` let (tx, rx) = mpsc::channel(); -``` -``` - -``` - -``` thread::spawn(move || { -``` - -``` let vals = vec![ -``` - -``` String::from("hi"), -``` - -``` String::from("from"), -``` - -``` String::from("the"), -``` - -``` String::from("thread"), -``` - -``` ]; -``` -``` - -``` - -``` for val in vals { -``` - -``` tx.send(val).unwrap(); -``` - -``` thread::sleep(Duration::from_secs(1)); -``` - -``` } -``` - -``` }); -``` -``` - -``` - -``` for received in rx { -``` - -``` println!("Got: {received}"); -``` - -``` } -``` - -``` } ``` -Sending multiple messages and pausing between each one +Listing 16-10: Sending multiple messages and pausing between each one This time, the spawned thread has a vector of strings that we want to send to the main thread. We iterate over them, sending each individually, and pause @@ -1377,17 +702,8 @@ with a one-second pause in between each line: ``` Got: hi -``` - -``` Got: from -``` - -``` Got: the -``` - -``` Got: thread ``` @@ -1397,166 +713,55 @@ the spawned thread. ### Creating Multiple Producers by Cloning the Transmitter -Earlier we mentioned that `mpsc` was an acronym for *multiple producer,* -*single consumer*. Let’s put `mpsc` to use and expand the code in Listing 16-10 -to create multiple threads that all send values to the same receiver. We can do -so by cloning the transmitter, as shown in Listing 16-11. +Earlier we mentioned that `mpsc` was an acronym for *multiple producer, single +consumer*. Let’s put `mpsc` to use and expand the code in Listing 16-10 to +create multiple threads that all send values to the same receiver. We can do so +by cloning the transmitter, as shown in Listing 16-11. Filename: src/main.rs ``` --snip-- -``` -``` - -``` - -``` let (tx, rx) = mpsc::channel(); -``` -``` - -``` - -``` let tx1 = tx.clone(); -``` - -``` thread::spawn(move || { -``` - -``` let vals = vec![ -``` - -``` String::from("hi"), -``` - -``` String::from("from"), -``` - -``` String::from("the"), -``` - -``` String::from("thread"), -``` - -``` ]; -``` -``` - -``` - -``` for val in vals { -``` - -``` tx1.send(val).unwrap(); -``` - -``` thread::sleep(Duration::from_secs(1)); -``` - -``` } -``` - -``` }); -``` -``` - -``` - -``` thread::spawn(move || { -``` - -``` let vals = vec![ -``` - -``` String::from("more"), -``` - -``` String::from("messages"), -``` - -``` String::from("for"), -``` - -``` String::from("you"), -``` - -``` ]; -``` -``` - -``` - -``` for val in vals { -``` - -``` tx.send(val).unwrap(); -``` - -``` thread::sleep(Duration::from_secs(1)); -``` - -``` } -``` - -``` }); -``` -``` - -``` - -``` for received in rx { -``` - -``` println!("Got: {received}"); -``` - -``` } -``` -``` - -``` - -``` --snip-- ``` -Sending multiple messages from multiple producers +Listing 16-11: Sending multiple messages from multiple producers This time, before we create the first spawned thread, we call `clone` on the transmitter. This will give us a new transmitter we can pass to the first @@ -1567,33 +772,12 @@ When you run the code, your output should look something like this: ``` Got: hi -``` - -``` Got: more -``` - -``` Got: from -``` - -``` Got: messages -``` - -``` Got: for -``` - -``` Got: the -``` - -``` Got: thread -``` - -``` Got: you ``` @@ -1663,53 +847,21 @@ Filename: src/main.rs ``` use std::sync::Mutex; -``` -``` - -``` - -``` fn main() { -``` - -``` 1 let m = Mutex::new(5); -``` -``` - -``` - -``` { -``` - -``` 2 let mut num = m.lock().unwrap(); -``` - -``` 3 *num = 6; -``` - -``` 4 } -``` -``` - -``` - -``` 5 println!("m = {:?}", m); -``` - -``` } ``` -Exploring the API of `Mutex` in a single-threaded context for simplicity +Listing 16-12: Exploring the API of `Mutex` in a single-threaded context for +simplicity As with many types, we create a `Mutex` using the associated function `new` [1]. To access the data inside the mutex, we use the `lock` method to acquire @@ -1752,93 +904,30 @@ Filename: src/main.rs ``` use std::sync::Mutex; -``` - -``` use std::thread; -``` -``` - -``` - -``` fn main() { -``` - -``` 1 let counter = Mutex::new(0); -``` - -``` let mut handles = vec![]; -``` -``` - -``` - -``` 2 for _ in 0..10 { -``` - -``` 3 let handle = thread::spawn(move || { -``` - -``` 4 let mut num = counter.lock().unwrap(); -``` -``` - -``` - -``` 5 *num += 1; -``` - -``` }); -``` - -``` 6 handles.push(handle); -``` - -``` } -``` -``` - -``` - -``` for handle in handles { -``` - -``` 7 handle.join().unwrap(); -``` - -``` } -``` -``` - -``` - -``` 8 println!("Result: {}", *counter.lock().unwrap()); -``` - -``` } ``` -Ten threads, each incrementing a counter guarded by a `Mutex` +Listing 16-13: Ten threads, each incrementing a counter guarded by a `Mutex` We create a `counter` variable to hold an `i32` inside a `Mutex` [1], as we did in Listing 16-12. Next, we create 10 threads by iterating over a range of @@ -1857,49 +946,16 @@ We hinted that this example wouldn’t compile. Now let’s find out why! ``` error[E0382]: use of moved value: `counter` -``` - -``` --> src/main.rs:9:36 -``` - -``` | -``` - -``` 5 | let counter = Mutex::new(0); -``` - -``` | ------- move occurs because `counter` has type `Mutex`, which -``` - -``` does not implement the `Copy` trait -``` - -``` ... -``` - -``` 9 | let handle = thread::spawn(move || { -``` - -``` | ^^^^^^^ value moved into closure here, -``` - -``` in previous iteration of loop -``` - -``` 10 | let mut num = counter.lock().unwrap(); -``` - -``` | ------- use occurs due to use in closure ``` @@ -1919,178 +975,56 @@ Filename: src/main.rs ``` use std::rc::Rc; -``` - -``` use std::sync::Mutex; -``` - -``` use std::thread; -``` -``` - -``` - -``` fn main() { -``` - -``` let counter = Rc::new(Mutex::new(0)); -``` - -``` let mut handles = vec![]; -``` -``` - -``` - -``` for _ in 0..10 { -``` - -``` let counter = Rc::clone(&counter); -``` - -``` let handle = thread::spawn(move || { -``` - -``` let mut num = counter.lock().unwrap(); -``` -``` - -``` - -``` *num += 1; -``` - -``` }); -``` - -``` handles.push(handle); -``` - -``` } -``` -``` - -``` - -``` for handle in handles { -``` - -``` handle.join().unwrap(); -``` - -``` } -``` -``` - -``` - -``` println!("Result: {}", *counter.lock().unwrap()); -``` - -``` } ``` -Attempting to use `Rc` to allow multiple threads to own the `Mutex` +Listing 16-14: Attempting to use `Rc` to allow multiple threads to own the +`Mutex` Once again, we compile and get… different errors! The compiler is teaching us a lot. ``` error[E0277]: `Rc>` cannot be sent between threads safely 1 -``` - -``` --> src/main.rs:11:22 -``` - -``` | -``` - -``` 11 | let handle = thread::spawn(move || { -``` - -``` | ______________________^^^^^^^^^^^^^_- -``` - -``` | | | -``` - -``` | | `Rc>` cannot be sent between threads -``` - -``` safely -``` - -``` 12 | | let mut num = counter.lock().unwrap(); -``` - -``` 13 | | -``` - -``` 14 | | *num += 1; -``` - -``` 15 | | }); -``` - -``` | |_________- within this `[closure@src/main.rs:11:36: 15:10]` -``` - -``` | -``` - -``` = help: within `[closure@src/main.rs:11:36: 15:10]`, the trait `Send` is not -``` - -``` implemented for `Rc>` 2 -``` - -``` = note: required because it appears within the type -``` - -``` `[closure@src/main.rs:11:36: 15:10]` -``` - -``` note: required by a bound in `spawn` ``` @@ -2113,12 +1047,12 @@ 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 primitive that we won’t cover in detail here: see the standard -library documentation for `std::sync::atomic` for more details. At this point, -you just need to know that atomics work like primitive types but are safe to -share across threads. +concurrent situations. The *a* stands for *atomic*, meaning it’s an *atomically +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` for more details. At this point, you just +need to know that atomics work like primitive types but are safe to share +across threads. You might then wonder why all primitive types aren’t atomic and why standard library types aren’t implemented to use `Arc` by default. The reason is that @@ -2135,98 +1069,32 @@ Filename: src/main.rs ``` use std::sync::{Arc, Mutex}; -``` - -``` use std::thread; -``` -``` - -``` - -``` fn main() { -``` - -``` let counter = Arc::new(Mutex::new(0)); -``` - -``` let mut handles = vec![]; -``` -``` - -``` - -``` for _ in 0..10 { -``` - -``` let counter = Arc::clone(&counter); -``` - -``` let handle = thread::spawn(move || { -``` - -``` let mut num = counter.lock().unwrap(); -``` -``` - -``` - -``` *num += 1; -``` - -``` }); -``` - -``` handles.push(handle); -``` - -``` } -``` -``` - -``` - -``` for handle in handles { -``` - -``` handle.join().unwrap(); -``` - -``` } -``` -``` - -``` - -``` println!("Result: {}", *counter.lock().unwrap()); -``` - -``` } ``` -Using an `Arc` to wrap the `Mutex` to be able to share ownership across -multiple threads +Listing 16-15: Using an `Arc` to wrap the `Mutex` to be able to share +ownership across multiple threads This code will print the following: @@ -2294,9 +1162,9 @@ performance penalty. Therefore, Rust’s type system and trait bounds ensure that you can never accidentally send an `Rc` value across threads unsafely. When we tried to do -this in Listing 16-14, we got the error `the trait` ````Send```` `is not -implemented for` ````Rc>````. When we switched to `Arc`, which is -`Send`, the code compiled. +this in Listing 16-14, we got the error `the trait `Send` is not implemented +for `Rc>``. When we switched to `Arc`, which is `Send`, the code +compiled. Any type composed entirely of `Send` types is automatically marked as `Send` as well. Almost all primitive types are `Send`, aside from raw pointers, which diff --git a/nostarch/chapter17.md b/nostarch/chapter17.md index 8463010c..d5bddbbd 100644 --- a/nostarch/chapter17.md +++ b/nostarch/chapter17.md @@ -70,22 +70,13 @@ Filename: src/lib.rs ``` pub struct AveragedCollection { -``` - -``` list: Vec, -``` - -``` average: f64, -``` - -``` } ``` -An `AveragedCollection` struct that maintains a list of integers and the -average of the items in the collection +Listing 17-1: An `AveragedCollection` struct that maintains a list of integers +and the average of the items in the collection The struct is marked `pub` so that other code can use it, but the fields within the struct remain private. This is important in this case because we want to @@ -97,110 +88,35 @@ Filename: src/lib.rs ``` impl AveragedCollection { -``` - -``` pub fn add(&mut self, value: i32) { -``` - -``` self.list.push(value); -``` - -``` self.update_average(); -``` - -``` } -``` -``` - -``` - -``` pub fn remove(&mut self) -> Option { -``` - -``` let result = self.list.pop(); -``` - -``` match result { -``` - -``` Some(value) => { -``` - -``` self.update_average(); -``` - -``` Some(value) -``` - -``` } -``` - -``` None => None, -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` pub fn average(&self) -> f64 { -``` - -``` self.average -``` - -``` } -``` -``` - -``` - -``` fn update_average(&mut self) { -``` - -``` let total: i32 = self.list.iter().sum(); -``` - -``` self.average = total as f64 / self.list.len() as f64; -``` - -``` } -``` - -``` } ``` -Implementations of the public methods `add`, `remove`, and `average` on -`AveragedCollection` +Listing 17-2: Implementations of the public methods `add`, `remove`, and +`average` on `AveragedCollection` The public methods `add`, `remove`, and `average` are the only ways to access or modify data in an instance of `AveragedCollection`. When an item is added to @@ -260,17 +176,12 @@ child type to be used in the same places as the parent type. This is also called *polymorphism*, which means that you can substitute multiple objects for each other at runtime if they share certain characteristics. - -Unmatched: BoxType - > ### Polymorphism - - +> > To many people, polymorphism is synonymous with inheritance. But it’s actually a more general concept that refers to code that can work with data of multiple types. For inheritance, those types are generally subclasses. - - +> > Rust instead uses generics to abstract over different possible types and trait bounds to impose constraints on what those types must provide. This is sometimes called *bounded parametric polymorphism*. @@ -361,17 +272,11 @@ Filename: src/lib.rs ``` pub trait Draw { -``` - -``` fn draw(&self); -``` - -``` } ``` -Definition of the `Draw` trait +Listing 17-3: Definition of the `Draw` trait This syntax should look familiar from our discussions on how to define traits in Chapter 10. Next comes some new syntax: Listing 17-4 defines a struct named @@ -383,18 +288,12 @@ Filename: src/lib.rs ``` pub struct Screen { -``` - -``` pub components: Vec>, -``` - -``` } ``` -Definition of the `Screen` struct with a `components` field holding a vector of -trait objects that implement the `Draw` trait +Listing 17-4: Definition of the `Screen` struct with a `components` field +holding a vector of trait objects that implement the `Draw` trait On the `Screen` struct, we’ll define a method named `run` that will call the `draw` method on each of its `components`, as shown in Listing 17-5. @@ -403,33 +302,16 @@ Filename: src/lib.rs ``` impl Screen { -``` - -``` pub fn run(&self) { -``` - -``` for component in self.components.iter() { -``` - -``` component.draw(); -``` - -``` } -``` - -``` } -``` - -``` } ``` -A `run` method on `Screen` that calls the `draw` method on each component +Listing 17-5: A `run` method on `Screen` that calls the `draw` method on each +component This works differently from defining a struct that uses a generic type parameter with trait bounds. A generic type parameter can only be substituted @@ -442,62 +324,23 @@ Filename: src/lib.rs ``` pub struct Screen { -``` - -``` pub components: Vec, -``` - -``` } -``` -``` - -``` - -``` impl Screen -``` - -``` where -``` - -``` T: Draw, -``` - -``` { -``` - -``` pub fn run(&self) { -``` - -``` for component in self.components.iter() { -``` - -``` component.draw(); -``` - -``` } -``` - -``` } -``` - -``` } ``` -An alternate implementation of the `Screen` struct and its `run` method using -generics and trait bounds +Listing 17-6: An alternate implementation of the `Screen` struct and its `run` +method using generics and trait bounds This restricts us to a `Screen` instance that has a list of components all of type `Button` or all of type `TextField`. If you’ll only ever have homogeneous @@ -521,49 +364,19 @@ Filename: src/lib.rs ``` pub struct Button { -``` - -``` pub width: u32, -``` - -``` pub height: u32, -``` - -``` pub label: String, -``` - -``` } -``` -``` - -``` - -``` impl Draw for Button { -``` - -``` fn draw(&self) { -``` - -``` // code to actually draw a button -``` - -``` } -``` - -``` } ``` -A `Button` struct that implements the `Draw` trait +Listing 17-7: A `Button` struct that implements the `Draw` trait The `width`, `height`, and `label` fields on `Button` will differ from the fields on other components; for example, a `TextField` type might have those @@ -583,58 +396,22 @@ Filename: src/main.rs ``` use gui::Draw; -``` -``` - -``` - -``` struct SelectBox { -``` - -``` width: u32, -``` - -``` height: u32, -``` - -``` options: Vec, -``` - -``` } -``` -``` - -``` - -``` impl Draw for SelectBox { -``` - -``` fn draw(&self) { -``` - -``` // code to actually draw a select box -``` - -``` } -``` - -``` } ``` -Another crate using `gui` and implementing the `Draw` trait on a `SelectBox` -struct +Listing 17-8: Another crate using `gui` and implementing the `Draw` trait on a +`SelectBox` struct Our library’s user can now write their `main` function to create a `Screen` instance. To the `Screen` instance, they can add a `SelectBox` and a `Button` @@ -646,102 +423,33 @@ Filename: src/main.rs ``` use gui::{Button, Screen}; -``` -``` - -``` - -``` fn main() { -``` - -``` let screen = Screen { -``` - -``` components: vec![ -``` - -``` Box::new(SelectBox { -``` - -``` width: 75, -``` - -``` height: 10, -``` - -``` options: vec![ -``` - -``` String::from("Yes"), -``` - -``` String::from("Maybe"), -``` - -``` String::from("No"), -``` - -``` ], -``` - -``` }), -``` - -``` Box::new(Button { -``` - -``` width: 50, -``` - -``` height: 10, -``` - -``` label: String::from("OK"), -``` - -``` }), -``` - -``` ], -``` - -``` }; -``` -``` - -``` - -``` screen.run(); -``` - -``` } ``` -Using trait objects to store values of different types that implement the same -trait +Listing 17-9: Using trait objects to store values of different types that +implement the same trait When we wrote the library, we didn’t know that someone might add the `SelectBox` type, but our `Screen` implementation was able to operate on the @@ -749,10 +457,10 @@ new type and draw it because `SelectBox` implements the `Draw` trait, which means it implements the `draw` method. This concept—of being concerned only with the messages a value responds to -rather than the value’s concrete type—is similar to the concept of *duck* -*typing* in dynamically typed languages: if it walks like a duck and quacks -like a duck, then it must be a duck! In the implementation of `run` on `Screen` -in Listing 17-5, `run` doesn’t need to know what the concrete type of each +rather than the value’s concrete type—is similar to the concept of *duck +typing* in dynamically typed languages: if it walks like a duck and quacks like +a duck, then it must be a duck! In the implementation of `run` on `Screen` in +Listing 17-5, `run` doesn’t need to know what the concrete type of each component is. It doesn’t check whether a component is an instance of a `Button` or a `SelectBox`, it just calls the `draw` method on the component. By specifying `Box` as the type of the values in the `components` @@ -772,73 +480,29 @@ Filename: src/main.rs ``` use gui::Screen; -``` -``` - -``` - -``` fn main() { -``` - -``` let screen = Screen { -``` - -``` components: vec![Box::new(String::from("Hi"))], -``` - -``` }; -``` -``` - -``` - -``` screen.run(); -``` - -``` } ``` -Attempting to use a type that doesn’t implement the trait object’s trait +Listing 17-10: Attempting to use a type that doesn’t implement the trait +object’s trait We’ll get this error because `String` doesn’t implement the `Draw` trait: ``` error[E0277]: the trait bound `String: Draw` is not satisfied -``` - -``` --> src/main.rs:5:26 -``` - -``` | -``` - -``` 5 | components: vec![Box::new(String::from("Hi"))], -``` - -``` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Draw` is -``` - -``` not implemented for `String` -``` - -``` | -``` - -``` = note: required for the cast to the object type `dyn Draw` ``` @@ -914,61 +578,23 @@ Filename: src/main.rs ``` use blog::Post; -``` -``` - -``` - -``` fn main() { -``` - -``` 1 let mut post = Post::new(); -``` -``` - -``` - -``` 2 post.add_text("I ate a salad for lunch today"); -``` - -``` 3 assert_eq!("", post.content()); -``` -``` - -``` - -``` 4 post.request_review(); -``` - -``` 5 assert_eq!("", post.content()); -``` -``` - -``` - -``` 6 post.approve(); -``` - -``` 7 assert_eq!("I ate a salad for lunch today", post.content()); -``` - -``` } ``` -Code that demonstrates the desired behavior we want our `blog` crate to have +Listing 17-11: Code that demonstrates the desired behavior we want our `blog` +crate to have We want to allow the user to create a new draft blog post with `Post::new` [1]. We want to allow text to be added to the blog post [2]. If we try to get the @@ -1009,82 +635,28 @@ Filename: src/lib.rs ``` pub struct Post { -``` - -``` state: Option>, -``` - -``` content: String, -``` - -``` } -``` -``` - -``` - -``` impl Post { -``` - -``` pub fn new() -> Post { -``` - -``` Post { -``` - -``` 1 state: Some(Box::new(Draft {})), -``` - -``` 2 content: String::new(), -``` - -``` } -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` trait State {} -``` -``` - -``` - -``` struct Draft {} -``` -``` - -``` - -``` impl State for Draft {} ``` -Definition of a `Post` struct and a `new` function that creates a new `Post` -instance, a `State` trait, and a `Draft` struct +Listing 17-12: Definition of a `Post` struct and a `new` function that creates +a new `Post` instance, a `State` trait, and a `Draft` struct The `State` trait defines the behavior shared by different post states. The state objects are `Draft`, `PendingReview`, and `Published`, and they will all @@ -1113,29 +685,15 @@ Filename: src/lib.rs ``` impl Post { -``` - -``` --snip-- -``` - -``` pub fn add_text(&mut self, text: &str) { -``` - -``` self.content.push_str(text); -``` - -``` } -``` - -``` } ``` -Implementing the `add_text` method to add text to a post’s `content` +Listing 17-13: Implementing the `add_text` method to add text to a post’s +`content` The `add_text` method takes a mutable reference to `self` because we’re changing the `Post` instance that we’re calling `add_text` on. We then call @@ -1160,30 +718,15 @@ Filename: src/lib.rs ``` impl Post { -``` - -``` --snip-- -``` - -``` pub fn content(&self) -> &str { -``` - -``` "" -``` - -``` } -``` - -``` } ``` -Adding a placeholder implementation for the `content` method on `Post` that -always returns an empty string slice +Listing 17-14: Adding a placeholder implementation for the `content` method on +`Post` that always returns an empty string slice With this added `content` method, everything in Listing 17-11 up to the line at [3] works as intended. @@ -1197,117 +740,37 @@ Filename: src/lib.rs ``` impl Post { -``` - -``` --snip-- -``` - -``` 1 pub fn request_review(&mut self) { -``` - -``` 2 if let Some(s) = self.state.take() { -``` - -``` 3 self.state = Some(s.request_review()) -``` - -``` } -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` trait State { -``` - -``` 4 fn request_review(self: Box) -> Box; -``` - -``` } -``` -``` - -``` - -``` struct Draft {} -``` -``` - -``` - -``` impl State for Draft { -``` - -``` fn request_review(self: Box) -> Box { -``` - -``` 5 Box::new(PendingReview {}) -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` struct PendingReview {} -``` -``` - -``` - -``` impl State for PendingReview { -``` - -``` fn request_review(self: Box) -> Box { -``` - -``` 6 self -``` - -``` } -``` - -``` } ``` -Implementing `request_review` methods on `Post` and the `State` trait +Listing 17-15: Implementing `request_review` methods on `Post` and the `State` +trait We give `Post` a public method named `request_review` that will take a mutable reference to `self` [1]. Then we call an internal `request_review` method on @@ -1361,177 +824,51 @@ Filename: src/lib.rs ``` impl Post { -``` - -``` --snip-- -``` - -``` pub fn approve(&mut self) { -``` - -``` if let Some(s) = self.state.take() { -``` - -``` self.state = Some(s.approve()) -``` - -``` } -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` trait State { -``` - -``` fn request_review(self: Box) -> Box; -``` - -``` fn approve(self: Box) -> Box; -``` - -``` } -``` -``` - -``` - -``` struct Draft {} -``` -``` - -``` - -``` impl State for Draft { -``` - -``` --snip-- -``` - -``` fn approve(self: Box) -> Box { -``` - -``` 1 self -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` struct PendingReview {} -``` -``` - -``` - -``` impl State for PendingReview { -``` - -``` --snip-- -``` - -``` fn approve(self: Box) -> Box { -``` - -``` 2 Box::new(Published {}) -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` struct Published {} -``` -``` - -``` - -``` impl State for Published { -``` - -``` fn request_review(self: Box) -> Box { -``` - -``` self -``` - -``` } -``` -``` - -``` - -``` fn approve(self: Box) -> Box { -``` - -``` self -``` - -``` } -``` - -``` } ``` -Implementing the `approve` method on `Post` and the `State` trait +Listing 17-16: Implementing the `approve` method on `Post` and the `State` trait We add the `approve` method to the `State` trait and add a new struct that implements `State`, the `Published` state. @@ -1553,34 +890,16 @@ Filename: src/lib.rs ``` impl Post { -``` - -``` --snip-- -``` - -``` pub fn content(&self) -> &str { -``` - -``` self.state.as_ref().unwrap().content(self) -``` - -``` } -``` - -``` --snip-- -``` - -``` } ``` -Updating the `content` method on `Post` to delegate to a `content` method on -`State` +Listing 17-17: Updating the `content` method on `Post` to delegate to a +`content` method on `State` Because the goal is to keep all of these rules inside the structs that implement `State`, we call a `content` method on the value in `state` and pass @@ -1611,69 +930,24 @@ Filename: src/lib.rs ``` trait State { -``` - -``` --snip-- -``` - -``` fn content<'a>(&self, post: &'a Post) -> &'a str { -``` - -``` 1 "" -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` --snip-- -``` - -``` struct Published {} -``` -``` - -``` - -``` impl State for Published { -``` - -``` --snip-- -``` - -``` fn content<'a>(&self, post: &'a Post) -> &'a str { -``` - -``` 2 &post.content -``` - -``` } -``` - -``` } ``` -Adding the `content` method to the `State` trait +Listing 17-18: Adding the `content` method to the `State` trait We add a default implementation for the `content` method that returns an empty string slice [1]. That means we don’t need to implement `content` on the @@ -1689,12 +963,8 @@ And we’re done—all of Listing 17-11 now works! We’ve implemented the state pattern with the rules of the blog post workflow. The logic related to the rules lives in the state objects rather than being scattered throughout `Post`. - -Unmatched: BoxType - > ### Why Not An Enum? - - +> > You may have been wondering why we didn’t use an `enum` with the different possible post states as variants. That’s certainly a possible solution; try it and compare the end results to see which you prefer! One disadvantage of using @@ -1727,17 +997,18 @@ The implementation using the state pattern is easy to extend to add more functionality. To see the simplicity of maintaining code that uses the state pattern, try a few of these suggestions: +* Add a `reject` method that changes the post’s state from `PendingReview` back +to `Draft`. +* Require two calls to `approve` before the state can be changed to `Published`. +* Allow users to add text content only when a post is in the `Draft` state. +Hint: have the state object responsible for what might change about the content +but not responsible for modifying the `Post`. -Unmatched: ListBullet0 - -Unmatched: ListBullet0 - -Unmatched: ListBullet0 - One downside of the state pattern is that, because the states implement -the transitions between states, some of the states are coupled to each other. -If we add another state between `PendingReview` and `Published`, such as -`Scheduled`, we would have to change the code in `PendingReview` to transition -to `Scheduled` instead. It would be less work if `PendingReview` didn’t need to +One downside of the state pattern is that, because the states implement the +transitions between states, some of the states are coupled to each other. If we +add another state between `PendingReview` and `Published`, such as `Scheduled`, +we would have to change the code in `PendingReview` to transition to +`Scheduled` instead. It would be less work if `PendingReview` didn’t need to change with the addition of a new state, but that would mean switching to another design pattern. @@ -1774,25 +1045,10 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let mut post = Post::new(); -``` -``` - -``` - -``` post.add_text("I ate a salad for lunch today"); -``` - -``` assert_eq!("", post.content()); -``` - -``` } ``` @@ -1810,105 +1066,34 @@ Filename: src/lib.rs ``` pub struct Post { -``` - -``` content: String, -``` - -``` } -``` -``` - -``` - -``` pub struct DraftPost { -``` - -``` content: String, -``` - -``` } -``` -``` - -``` - -``` impl Post { -``` - -``` 1 pub fn new() -> DraftPost { -``` - -``` DraftPost { -``` - -``` content: String::new(), -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` 2 pub fn content(&self) -> &str { -``` - -``` &self.content -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` impl DraftPost { -``` - -``` 3 pub fn add_text(&mut self, text: &str) { -``` - -``` self.content.push_str(text); -``` - -``` } -``` - -``` } ``` -A `Post` with a `content` method and a `DraftPost` without a `content` method +Listing 17-19: A `Post` with a `content` method and a `DraftPost` without a +`content` method Both the `Post` and `DraftPost` structs have a private `content` field that stores the blog post text. The structs no longer have the `state` field because @@ -1941,87 +1126,30 @@ Filename: src/lib.rs ``` impl DraftPost { -``` - -``` --snip-- -``` - -``` pub fn request_review(self) -> PendingReviewPost { -``` - -``` PendingReviewPost { -``` - -``` content: self.content, -``` - -``` } -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` pub struct PendingReviewPost { -``` - -``` content: String, -``` - -``` } -``` -``` - -``` - -``` impl PendingReviewPost { -``` - -``` pub fn approve(self) -> Post { -``` - -``` Post { -``` - -``` content: self.content, -``` - -``` } -``` - -``` } -``` - -``` } ``` -A `PendingReviewPost` that gets created by calling `request_review` on -`DraftPost` and an `approve` method that turns a `PendingReviewPost` into a -published `Post` +Listing 17-20: A `PendingReviewPost` that gets created by calling +`request_review` on `DraftPost` and an `approve` method that turns a +`PendingReviewPost` into a published `Post` The `request_review` and `approve` methods take ownership of `self`, thus consuming the `DraftPost` and `PendingReviewPost` instances and transforming @@ -2047,57 +1175,22 @@ Filename: src/main.rs ``` use blog::Post; -``` -``` - -``` - -``` fn main() { -``` - -``` let mut post = Post::new(); -``` -``` - -``` - -``` post.add_text("I ate a salad for lunch today"); -``` -``` - -``` - -``` let post = post.request_review(); -``` -``` - -``` - -``` let post = post.approve(); -``` -``` - -``` - -``` assert_eq!("I ate a salad for lunch today", post.content()); -``` - -``` } ``` -Modifications to `main` to use the new implementation of the blog post workflow +Listing 17-21: Modifications to `main` to use the new implementation of the +blog post workflow The changes we needed to make to `main` to reassign `post` mean that this implementation doesn’t quite follow the object-oriented state pattern anymore: diff --git a/nostarch/chapter18.md b/nostarch/chapter18.md index 9c93ff8f..40c7f10a 100644 --- a/nostarch/chapter18.md +++ b/nostarch/chapter18.md @@ -18,6 +18,7 @@ control flow. A pattern consists of some combination of the following: * Variables * Wildcards * Placeholders + Some example patterns include `x`, `(a, 3)`, and `Some(Color::Red)`. In the contexts in which patterns are valid, these components describe the shape of data. Our program then matches values against the patterns to determine whether @@ -50,21 +51,9 @@ expression to run if the value matches that arm’s pattern, like this: ``` match VALUE { -``` - -``` PATTERN => EXPRESSION, -``` - -``` PATTERN => EXPRESSION, -``` - -``` PATTERN => EXPRESSION, -``` - -``` } ``` @@ -73,17 +62,8 @@ For example, here’s the `match` expression from Listing 6-5 that matches on an ``` match x { -``` - -``` None => None, -``` - -``` Some(i) => Some(i + 1), -``` - -``` } ``` @@ -124,89 +104,29 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let favorite_color: Option<&str> = None; -``` - -``` let is_tuesday = false; -``` - -``` let age: Result = "34".parse(); -``` -``` - -``` - -``` 1 if let Some(color) = favorite_color { -``` - -``` 2 println!( -``` - -``` "Using your favorite, {color}, as the background" -``` - -``` ); -``` - -``` 3 } else if is_tuesday { -``` - -``` 4 println!("Tuesday is green day!"); -``` - -``` 5 } else if let Ok(age) = age { -``` - -``` 6 if age > 30 { -``` - -``` 7 println!("Using purple as the background color"); -``` - -``` } else { -``` - -``` 8 println!("Using orange as the background color"); -``` - -``` } -``` - -``` 9 } else { -``` - -``` 10 println!("Using blue as the background color"); -``` - -``` } -``` - -``` } ``` -Mixing `if let`, `else if`, `else if let`, and `else` +Listing 18-1: Mixing `if let`, `else if`, `else if let`, and `else` If the user specifies a favorite color [1], that color is used as the background [2]. If no favorite color is specified and today is Tuesday [3], the @@ -239,44 +159,22 @@ Similar in construction to `if let`, the `while let` conditional loop allows a 18-2, we code a `while let` loop that uses a vector as a stack and prints the values in the vector in the opposite order in which they were pushed. +Filename: src/main.rs + ``` let mut stack = Vec::new(); -``` -``` - -``` - -``` stack.push(1); -``` - -``` stack.push(2); -``` - -``` stack.push(3); -``` -``` - -``` - -``` while let Some(top) = stack.pop() { -``` - -``` println!("{top}"); -``` - -``` } ``` -Using a `while let` loop to print values for as long as `stack.pop()` returns -`Some` +Listing 18-2: Using a `while let` loop to print values for as long as +`stack.pop()` returns `Some` This example prints `3`, `2`, and then `1`. The `pop` method takes the last element out of the vector and returns `Some(value)`. If the vector is empty, @@ -291,39 +189,23 @@ pattern. For example, in `for x in y`, the `x` is the pattern. Listing 18-3 demonstrates how to use a pattern in a `for` loop to *destructure*, or break apart, a tuple as part of the `for` loop. +Filename: src/main.rs + ``` let v = vec!['a', 'b', 'c']; -``` -``` - -``` - -``` for (index, value) in v.iter().enumerate() { -``` - -``` println!("{value} is at index {index}"); -``` - -``` } ``` -Using a pattern in a `for` loop to destructure a tuple +Listing 18-3: Using a pattern in a `for` loop to destructure a tuple The code in Listing 18-3 will print the following: ``` a is at index 0 -``` - -``` b is at index 1 -``` - -``` c is at index 2 ``` @@ -366,7 +248,8 @@ To see the pattern-matching aspect of `let` more clearly, consider Listing let (x, y, z) = (1, 2, 3); ``` -Using a pattern to destructure a tuple and create three variables at once +Listing 18-4: Using a pattern to destructure a tuple and create three variables +at once Here, we match a tuple against a pattern. Rust compares the value `(1, 2, 3)` to the pattern `(x, y, z)` and sees that the value matches the pattern, in that @@ -383,52 +266,22 @@ elements into two variables, which won’t work. let (x, y) = (1, 2, 3); ``` -Incorrectly constructing a pattern whose variables don’t match the number of -elements in the tuple +Listing 18-5: Incorrectly constructing a pattern whose variables don’t match +the number of elements in the tuple Attempting to compile this code results in this type error: ``` error[E0308]: mismatched types -``` - -``` --> src/main.rs:2:9 -``` - -``` | -``` - -``` 2 | let (x, y) = (1, 2, 3); -``` - -``` | ^^^^^^ --------- this expression has type `({integer}, {integer}, -``` - -``` {integer})` -``` - -``` | | -``` - -``` | expected a tuple with 3 elements, found one with 2 elements -``` - -``` | -``` - -``` = note: expected tuple `({integer}, {integer}, {integer})` -``` - -``` found tuple `(_, _)` ``` @@ -446,17 +299,11 @@ declares a function named `foo` that takes one parameter named `x` of type ``` fn foo(x: i32) { -``` - -``` // code goes here -``` - -``` } ``` -A function signature using patterns in the parameters +Listing 18-6: A function signature using patterns in the parameters The `x` part is a pattern! As we did with `let`, we could match a tuple in a function’s arguments to the pattern. Listing 18-7 splits the values in a tuple @@ -466,37 +313,16 @@ Filename: src/main.rs ``` fn print_coordinates(&(x, y): &(i32, i32)) { -``` - -``` println!("Current location: ({x}, {y})"); -``` - -``` } -``` -``` - -``` - -``` fn main() { -``` - -``` let point = (3, 5); -``` - -``` print_coordinates(&point); -``` - -``` } ``` -A function with parameters that destructure a tuple +Listing 18-7: A function with parameters that destructure a tuple This code prints `Current location: (3, 5)`. The values `&(3, 5)` match the pattern `&(x, y)`, so `x` is the value `3` and `y` is the value `5`. @@ -543,7 +369,7 @@ pattern. As you might expect, this code will not compile. let Some(x) = some_option_value; ``` -Attempting to use a refutable pattern with `let` +Listing 18-8: Attempting to use a refutable pattern with `let` If `some_option_value` were a `None` value, it would fail to match the pattern `Some(x)`, meaning the pattern is refutable. However, the `let` statement can @@ -553,61 +379,19 @@ use a refutable pattern where an irrefutable pattern is required: ``` error[E0005]: refutable pattern in local binding: `None` not covered -``` - -``` --> src/main.rs:3:9 -``` - -``` | -``` - -``` 3 | let Some(x) = some_option_value; -``` - -``` | ^^^^^^^ pattern `None` not covered -``` - -``` | -``` - -``` = note: `let` bindings require an "irrefutable pattern", like a `struct` or -``` - -``` an `enum` with only one variant -``` - -``` = note: for more information, visit -``` - -``` https://doc.rust-lang.org/book/ch18-02-refutability.html -``` - -``` = note: the matched value is of type `Option` -``` - -``` help: you might want to use `if let` to ignore the variant that isn't matched -``` - -``` | -``` - -``` 3 | let x = if let Some(x) = some_option_value { x } else { todo!() }; -``` - -``` | ++++++++++ ++++++++++++++++++++++ ``` @@ -622,17 +406,12 @@ the code in the curly brackets, giving it a way to continue validly. Listing ``` if let Some(x) = some_option_value { -``` - -``` println!("{x}"); -``` - -``` } ``` -Using `if let` and a block with refutable patterns instead of `let` +Listing 18-9: Using `if let` and a block with refutable patterns instead of +`let` We’ve given the code an out! This code is perfectly valid, although it means we cannot use an irrefutable pattern without receiving an error. If we give `if @@ -641,58 +420,25 @@ the compiler will give a warning. ``` if let x = 5 { -``` - -``` println!("{x}"); -``` - -``` }; ``` -Attempting to use an irrefutable pattern with `if let` +Listing 18-10: Attempting to use an irrefutable pattern with `if let` Rust complains that it doesn’t make sense to use `if let` with an irrefutable pattern: ``` warning: irrefutable `if let` pattern -``` - -``` --> src/main.rs:2:8 -``` - -``` | -``` - -``` 2 | if let x = 5 { -``` - -``` | ^^^^^^^^^ -``` - -``` | -``` - -``` = note: `#[warn(irrefutable_let_patterns)]` on by default -``` - -``` = note: this pattern will always match, so the `if let` is -``` - -``` useless -``` - -``` = help: consider replacing the `if let` with a `let` ``` @@ -716,35 +462,16 @@ why and when you might want to use each one. As you saw in Chapter 6, you can match patterns against literals directly. The following code gives some examples: +Filename: src/main.rs + ``` let x = 1; -``` -``` - -``` - -``` match x { -``` - -``` 1 => println!("one"), -``` - -``` 2 => println!("two"), -``` - -``` 3 => println!("three"), -``` - -``` _ => println!("anything"), -``` - -``` } ``` @@ -769,53 +496,21 @@ Filename: src/main.rs ``` fn main() { -``` - -``` 1 let x = Some(5); -``` - -``` 2 let y = 10; -``` -``` - -``` - -``` match x { -``` - -``` 3 Some(50) => println!("Got 50"), -``` - -``` 4 Some(y) => println!("Matched, y = {y}"), -``` - -``` 5 _ => println!("Default case, x = {:?}", x), -``` - -``` } -``` -``` - -``` - -``` 6 println!("at the end: x = {:?}, y = {y}", x); -``` - -``` } ``` -A `match` expression with an arm that introduces a shadowed variable `y` +Listing 18-11: A `match` expression with an arm that introduces a shadowed +variable `y` Let’s walk through what happens when the `match` expression runs. The pattern in the first match arm [3] doesn’t match the defined value of `x` [1], so the @@ -853,63 +548,34 @@ the value of `x` against the match arms, the first of which has an *or* option, meaning if the value of `x` matches either of the values in that arm, that arm’s code will run: +Filename: src/main.rs + ``` let x = 1; -``` -``` - -``` - -``` match x { -``` - -``` 1 | 2 => println!("one or two"), -``` - -``` 3 => println!("three"), -``` - -``` _ => println!("anything"), -``` - -``` } ``` +This code prints `one or two`. -Unmatched: BodyContinued - ### Matching Ranges of Values with ..= +### Matching Ranges of Values with ..= The `..=` syntax allows us to match to an inclusive range of values. In the following code, when a pattern matches any of the values within the given range, that arm will execute: +Filename: src/main.rs + ``` let x = 5; -``` -``` - -``` - -``` match x { -``` - -``` 1..=5 => println!("one through five"), -``` - -``` _ => println!("something else"), -``` - -``` } ``` @@ -925,31 +591,15 @@ numeric values, ranges are only allowed with numeric or `char` values. Here is an example using ranges of `char` values: +Filename: src/main.rs + ``` let x = 'c'; -``` -``` - -``` - -``` match x { -``` - -``` 'a'..='j' => println!("early ASCII letter"), -``` - -``` 'k'..='z' => println!("late ASCII letter"), -``` - -``` _ => println!("something else"), -``` - -``` } ``` @@ -970,53 +620,20 @@ Filename: src/main.rs ``` struct Point { -``` - -``` x: i32, -``` - -``` y: i32, -``` - -``` } -``` -``` - -``` - -``` fn main() { -``` - -``` let p = Point { x: 0, y: 7 }; -``` -``` - -``` - -``` let Point { x: a, y: b } = p; -``` - -``` assert_eq!(0, a); -``` - -``` assert_eq!(7, b); -``` - -``` } ``` -Destructuring a struct’s fields into separate variables +Listing 18-12: Destructuring a struct’s fields into separate variables This code creates the variables `a` and `b` that match the values of the `x` and `y` fields of the `p` struct. This example shows that the names of the @@ -1034,53 +651,20 @@ Filename: src/main.rs ``` struct Point { -``` - -``` x: i32, -``` - -``` y: i32, -``` - -``` } -``` -``` - -``` - -``` fn main() { -``` - -``` let p = Point { x: 0, y: 7 }; -``` -``` - -``` - -``` let Point { x, y } = p; -``` - -``` assert_eq!(0, x); -``` - -``` assert_eq!(7, y); -``` - -``` } ``` -Destructuring struct fields using struct field shorthand +Listing 18-13: Destructuring struct fields using struct field shorthand This code creates the variables `x` and `y` that match the `x` and `y` fields of the `p` variable. The outcome is that the variables `x` and `y` contain the @@ -1099,49 +683,19 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let p = Point { x: 0, y: 7 }; -``` -``` - -``` - -``` match p { -``` - -``` Point { x, y: 0 } => println!("On the x axis at {x}"), -``` - -``` Point { x: 0, y } => println!("On the y axis at {y}"), -``` - -``` Point { x, y } => { -``` - -``` println!("On neither axis: ({x}, {y})"); -``` - -``` } -``` - -``` } -``` - -``` } ``` -Destructuring and matching literal values in one pattern +Listing 18-14: Destructuring and matching literal values in one pattern The first arm will match any point that lies on the `x` axis by specifying that the `y` field matches if its value matches the literal `0`. The pattern still @@ -1171,121 +725,37 @@ Filename: src/main.rs ``` enum Message { -``` - -``` Quit, -``` - -``` Move { x: i32, y: i32 }, -``` - -``` Write(String), -``` - -``` ChangeColor(i32, i32, i32), -``` - -``` } -``` -``` - -``` - -``` fn main() { -``` - -``` 1 let msg = Message::ChangeColor(0, 160, 255); -``` -``` - -``` - -``` match msg { -``` - -``` 2 Message::Quit => { -``` - -``` println!( -``` - -``` "The Quit variant has no data to destructure." -``` - -``` ); -``` - -``` } -``` - -``` 3 Message::Move { x, y } => { -``` - -``` println!( -``` - -``` "Move in the x dir {x}, in the y dir {y}" -``` - -``` ); -``` - -``` } -``` - -``` 4 Message::Write(text) => { -``` - -``` println!("Text message: {text}"); -``` - -``` } -``` - -``` 5 Message::ChangeColor(r, g, b) => println!( -``` - -``` "Change color to red {r}, green {g}, and blue {b}" -``` - -``` ), -``` - -``` } -``` - -``` } ``` -Destructuring enum variants that hold different kinds of values +Listing 18-15: Destructuring enum variants that hold different kinds of values This code will print `Change color to red 0, green 160, and blue 255`. Try changing the value of `msg` [1] to see the code from the other arms run. @@ -1313,107 +783,37 @@ but matching can work on nested items too! For example, we can refactor the code in Listing 18-15 to support RGB and HSV colors in the `ChangeColor` message, as shown in Listing 18-16. +Filename: src/main.rs + ``` enum Color { -``` - -``` Rgb(i32, i32, i32), -``` - -``` Hsv(i32, i32, i32), -``` - -``` } -``` -``` - -``` - -``` enum Message { -``` - -``` Quit, -``` - -``` Move { x: i32, y: i32 }, -``` - -``` Write(String), -``` - -``` ChangeColor(Color), -``` - -``` } -``` -``` - -``` - -``` fn main() { -``` - -``` let msg = Message::ChangeColor(Color::Hsv(0, 160, 255)); -``` -``` - -``` - -``` match msg { -``` - -``` Message::ChangeColor(Color::Rgb(r, g, b)) => println!( -``` - -``` "Change color to red {r}, green {g}, and blue {b}" -``` - -``` ), -``` - -``` Message::ChangeColor(Color::Hsv(h, s, v)) => println!( -``` - -``` "Change color to hue {h}, saturation {s}, value {v}" -``` - -``` ), -``` - -``` _ => (), -``` - -``` } -``` - -``` } ``` -Matching on nested enums +Listing 18-16: Matching on nested enums The pattern of the first arm in the `match` expression matches a `Message::ChangeColor` enum variant that contains a `Color::Rgb` variant; then @@ -1430,9 +830,6 @@ tuples inside a tuple and destructure all the primitive values out: ``` let ((feet, inches), Point { x, y }) = -``` - -``` ((3, 10), Point { x: 3, y: -10 }); ``` @@ -1463,33 +860,15 @@ Filename: src/main.rs ``` fn foo(_: i32, y: i32) { -``` - -``` println!("This code only uses the y parameter: {y}"); -``` - -``` } -``` -``` - -``` - -``` fn main() { -``` - -``` foo(3, 4); -``` - -``` } ``` -Using `_` in a function signature +Listing 18-17: Using `_` in a function signature This code will completely ignore the value `3` passed as the first argument, and will print `This code only uses the y parameter: 4`. @@ -1511,60 +890,26 @@ responsible for managing a setting’s value. The business requirements are that the user should not be allowed to overwrite an existing customization of a setting but can unset the setting and give it a value if it is currently unset. +Filename: src/main.rs + ``` let mut setting_value = Some(5); -``` - -``` let new_setting_value = Some(10); -``` -``` - -``` - -``` match (setting_value, new_setting_value) { -``` - -``` (Some(_), Some(_)) => { -``` - -``` println!("Can't overwrite an existing customized value"); -``` - -``` } -``` - -``` _ => { -``` - -``` setting_value = new_setting_value; -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` println!("setting is {:?}", setting_value); ``` -Using an underscore within patterns that match `Some` variants when we don’t -need to use the value inside the `Some` +Listing 18-18: Using an underscore within patterns that match `Some` variants +when we don’t need to use the value inside the `Some` This code will print `Can't overwrite an existing customized value` and then `setting is Some(5)`. In the first match arm, we don’t need to match on or use @@ -1581,35 +926,19 @@ We can also use underscores in multiple places within one pattern to ignore particular values. Listing 18-19 shows an example of ignoring the second and fourth values in a tuple of five items. +Filename: src/main.rs + ``` let numbers = (2, 4, 8, 16, 32); -``` -``` - -``` - -``` match numbers { -``` - -``` (first, _, third, _, fifth) => { -``` - -``` println!("Some numbers: {first}, {third}, {fifth}"); -``` - -``` } -``` - -``` } ``` -Ignoring multiple parts of a tuple +Listing 18-19: Ignoring multiple parts of a tuple This code will print `Some numbers: 2, 8, 32`, and the values `4` and `16` will be ignored. @@ -1628,22 +957,13 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let _x = 5; -``` - -``` let y = 10; -``` - -``` } ``` -Starting a variable name with an underscore to avoid getting unused variable -warnings +Listing 18-20: Starting a variable name with an underscore to avoid getting +unused variable warnings Here, we get a warning about not using the variable `y`, but we don’t get a warning about not using `_x`. @@ -1653,71 +973,39 @@ that starts with an underscore. The syntax `_x` still binds the value to the variable, whereas `_` doesn’t bind at all. To show a case where this distinction matters, Listing 18-21 will provide us with an error. +Filename: src/main.rs + ``` let s = Some(String::from("Hello!")); -``` -``` - -``` - -``` if let Some(_s) = s { -``` - -``` println!("found a string"); -``` - -``` } -``` -``` - -``` - -``` println!("{:?}", s); ``` -An unused variable starting with an underscore still binds the value, which -might take ownership of the value. +Listing 18-21: An unused variable starting with an underscore still binds the +value, which might take ownership of the value. We’ll receive an error because the `s` value will still be moved into `_s`, which prevents us from using `s` again. However, using the underscore by itself doesn’t ever bind to the value. Listing 18-22 will compile without any errors because `s` doesn’t get moved into `_`. +Filename: src/main.rs + ``` let s = Some(String::from("Hello!")); -``` -``` - -``` - -``` if let Some(_) = s { -``` - -``` println!("found a string"); -``` - -``` } -``` -``` - -``` - -``` println!("{:?}", s); ``` -Using an underscore does not bind the value. +Listing 18-22: Using an underscore does not bind the value. This code works just fine because we never bind `s` to anything; it isn’t moved. @@ -1731,51 +1019,23 @@ explicitly matched in the rest of the pattern. In Listing 18-23, we have a `match` expression, we want to operate only on the `x` coordinate and ignore the values in the `y` and `z` fields. +Filename: src/main.rs + ``` struct Point { -``` - -``` x: i32, -``` - -``` y: i32, -``` - -``` z: i32, -``` - -``` } -``` -``` - -``` - -``` let origin = Point { x: 0, y: 0, z: 0 }; -``` -``` - -``` - -``` match origin { -``` - -``` Point { x, .. } => println!("x is {x}"), -``` - -``` } ``` -Ignoring all fields of a `Point` except for `x` by using `..` +Listing 18-23: Ignoring all fields of a `Point` except for `x` by using `..` We list the `x` value and then just include the `..` pattern. This is quicker than having to list `y: _` and `z: _`, particularly when we’re working with @@ -1789,41 +1049,18 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let numbers = (2, 4, 8, 16, 32); -``` -``` - -``` - -``` match numbers { -``` - -``` (first, .., last) => { -``` - -``` println!("Some numbers: {first}, {last}"); -``` - -``` } -``` - -``` } -``` - -``` } ``` -Matching only the first and last values in a tuple and ignoring all other values +Listing 18-24: Matching only the first and last values in a tuple and ignoring +all other values In this code, the first and last values are matched with `first` and `last`. The `..` will match and ignore everything in the middle. @@ -1837,69 +1074,27 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let numbers = (2, 4, 8, 16, 32); -``` -``` - -``` - -``` match numbers { -``` - -``` (.., second, ..) => { -``` - -``` println!("Some numbers: {second}"); -``` - -``` }, -``` - -``` } -``` - -``` } ``` -An attempt to use `..` in an ambiguous way +Listing 18-25: An attempt to use `..` in an ambiguous way When we compile this example, we get this error: ``` error: `..` can only be used once per tuple pattern -``` - -``` --> src/main.rs:5:22 -``` - -``` | -``` - -``` 5 | (.., second, ..) => { -``` - -``` | -- ^^ can only be used once per tuple pattern -``` - -``` | | -``` - -``` | previously used here ``` @@ -1921,35 +1116,19 @@ The condition can use variables created in the pattern. Listing 18-26 shows a `match` where the first arm has the pattern `Some(x)` and also has a match guard of `if x % 2 == 0` (which will be `true` if the number is even). +Filename: src/main.rs + ``` let num = Some(4); -``` -``` - -``` - -``` match num { -``` - -``` Some(x) if x % 2 == 0 => println!("The number {x} is even"), -``` - -``` Some(x) => println!("The number {x} is odd"), -``` - -``` None => (), -``` - -``` } ``` -Adding a match guard to a pattern +Listing 18-26: Adding a match guard to a pattern This example will print `The number 4 is even`. When `num` is compared to the pattern in the first arm, it matches because `Some(4)` matches `Some(x)`. Then @@ -1977,53 +1156,20 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let x = Some(5); -``` - -``` let y = 10; -``` -``` - -``` - -``` match x { -``` - -``` Some(50) => println!("Got 50"), -``` - -``` Some(n) if n == y => println!("Matched, n = {n}"), -``` - -``` _ => println!("Default case, x = {:?}", x), -``` - -``` } -``` -``` - -``` - -``` println!("at the end: x = {:?}, y = {y}", x); -``` - -``` } ``` -Using a match guard to test for equality with an outer variable +Listing 18-27: Using a match guard to test for equality with an outer variable This code will now print `Default case, x = Some(5)`. The pattern in the second match arm doesn’t introduce a new variable `y` that would shadow the outer `y`, @@ -2044,35 +1190,19 @@ guard. The important part of this example is that the `if y` match guard applies to `4`, `5`, *and* `6`, even though it might look like `if y` only applies to `6`. +Filename: src/main.rs + ``` let x = 4; -``` - -``` let y = false; -``` -``` - -``` - -``` match x { -``` - -``` 4 | 5 | 6 if y => println!("yes"), -``` - -``` _ => println!("no"), -``` - -``` } ``` -Combining multiple patterns with a match guard +Listing 18-28: Combining multiple patterns with a match guard The match condition states that the arm only matches if the value of `x` is equal to `4`, `5`, or `6` *and* if `y` is `true`. When this code runs, the @@ -2087,9 +1217,9 @@ pattern behaves like this: (4 | 5 | 6) if y => ... ``` +rather than this: -Unmatched: BodyContinued - ``` +``` 4 | 5 | (6 if y) => ... ``` @@ -2107,67 +1237,27 @@ want to bind the value to the variable `id_variable` so we can use it in the code associated with the arm. We could name this variable `id`, the same as the field, but for this example we’ll use a different name. +Filename: src/main.rs + ``` enum Message { -``` - -``` Hello { id: i32 }, -``` - -``` } -``` -``` - -``` - -``` let msg = Message::Hello { id: 5 }; -``` -``` - -``` - -``` match msg { -``` - -``` Message::Hello { -``` - -``` id: id_variable @ 3..=7, -``` - -``` } => println!("Found an id in range: {id_variable}"), -``` - -``` Message::Hello { id: 10..=12 } => { -``` - -``` println!("Found an id in another range") -``` - -``` } -``` - -``` Message::Hello { id } => println!("Some other id: {id}"), -``` - -``` } ``` -Using `@` to bind to a value in a pattern while also testing it +Listing 18-29: Using `@` to bind to a value in a pattern while also testing it This example will print `Found an id in range: 5`. By specifying `id_variable @` before the range `3..=7`, we’re capturing whatever value matched the range diff --git a/nostarch/chapter19.md b/nostarch/chapter19.md index 6d32a0f4..ec98f2bc 100644 --- a/nostarch/chapter19.md +++ b/nostarch/chapter19.md @@ -26,6 +26,7 @@ syntax, supertraits, and the newtype pattern in relation to traits and dynamically sized types * Advanced functions and closures: function pointers and returning closures * Macros: ways to define code that defines more code at compile time + It’s a panoply of Rust features with something for everyone! Let’s dive in! ## Unsafe Rust @@ -111,6 +112,7 @@ mutable pointers or multiple mutable pointers to the same location * Aren’t guaranteed to point to valid memory * Are allowed to be null * Don’t implement any automatic cleanup + By opting out of having Rust enforce these guarantees, you can give up guaranteed safety in exchange for greater performance or the ability to interface with another language or hardware where Rust’s guarantees don’t apply. @@ -120,21 +122,12 @@ references. ``` let mut num = 5; -``` -``` - -``` - -``` let r1 = &num as *const i32; -``` - -``` let r2 = &mut num as *mut i32; ``` -Creating raw pointers from references +Listing 19-1: Creating raw pointers from references Notice that we don’t include the `unsafe` keyword in this code. We can create raw pointers in safe code; we just can’t dereference raw pointers outside an @@ -156,13 +149,10 @@ but it is possible. ``` let address = 0x012345usize; -``` - -``` let r = address as *const i32; ``` -Creating a raw pointer to an arbitrary memory address +Listing 19-2: Creating a raw pointer to an arbitrary memory address Recall that we can create raw pointers in safe code, but we can’t *dereference* raw pointers and read the data being pointed to. In Listing 19-3, we use the @@ -170,41 +160,17 @@ dereference operator `*` on a raw pointer that requires an `unsafe` block. ``` let mut num = 5; -``` -``` - -``` - -``` let r1 = &num as *const i32; -``` - -``` let r2 = &mut num as *mut i32; -``` -``` - -``` - -``` unsafe { -``` - -``` println!("r1 is: {}", *r1); -``` - -``` println!("r2 is: {}", *r2); -``` - -``` } ``` -Dereferencing raw pointers within an `unsafe` block +Listing 19-3: Dereferencing raw pointers within an `unsafe` block Creating a pointer does no harm; it’s only when we try to access the value that it points at that we might end up dealing with an invalid value. @@ -241,21 +207,9 @@ body: ``` unsafe fn dangerous() {} -``` -``` - -``` - -``` unsafe { -``` - -``` dangerous(); -``` - -``` } ``` @@ -264,37 +218,13 @@ try to call `dangerous` without the `unsafe` block, we’ll get an error: ``` error[E0133]: call to unsafe function is unsafe and requires -``` - -``` unsafe function or block -``` - -``` --> src/main.rs:4:5 -``` - -``` | -``` - -``` 4 | dangerous(); -``` - -``` | ^^^^^^^^^^^ call to unsafe function -``` - -``` | -``` - -``` = note: consult the function's documentation for information on -``` - -``` how to avoid undefined behavior ``` @@ -318,37 +248,16 @@ argument. Listing 19-4 shows how to use `split_at_mut`. ``` let mut v = vec![1, 2, 3, 4, 5, 6]; -``` -``` - -``` - -``` let r = &mut v[..]; -``` -``` - -``` - -``` let (a, b) = r.split_at_mut(3); -``` -``` - -``` - -``` assert_eq!(a, &mut [1, 2, 3]); -``` - -``` assert_eq!(b, &mut [4, 5, 6]); ``` -Using the safe `split_at_mut` function +Listing 19-4: Using the safe `split_at_mut` function We can’t implement this function using only safe Rust. An attempt might look something like Listing 19-5, which won’t compile. For simplicity, we’ll @@ -357,45 +266,18 @@ of `i32` values rather than for a generic type `T`. ``` fn split_at_mut( -``` - -``` values: &mut [i32], -``` - -``` mid: usize, -``` - -``` ) -> (&mut [i32], &mut [i32]) { -``` - -``` let len = values.len(); -``` -``` - -``` - -``` assert!(mid <= len); -``` -``` - -``` - -``` (&mut values[..mid], &mut values[mid..]) -``` - -``` } ``` -An attempted implementation of `split_at_mut` using only safe Rust +Listing 19-5: An attempted implementation of `split_at_mut` using only safe Rust This function first gets the total length of the slice. Then it asserts that the index given as a parameter is within the slice by checking whether it’s @@ -411,49 +293,16 @@ When we try to compile the code in Listing 19-5, we’ll get an error: ``` error[E0499]: cannot borrow `*values` as mutable more than once at a time -``` - -``` --> src/main.rs:9:31 -``` - -``` | -``` - -``` 2 | values: &mut [i32], -``` - -``` | - let's call the lifetime of this reference `'1` -``` - -``` ... -``` - -``` 9 | (&mut values[..mid], &mut values[mid..]) -``` - -``` | --------------------------^^^^^^-------- -``` - -``` | | | | -``` - -``` | | | second mutable borrow occurs here -``` - -``` | | first mutable borrow occurs here -``` - -``` | returning this value requires that `*values` is borrowed for `'1` ``` @@ -468,77 +317,27 @@ to unsafe functions to make the implementation of `split_at_mut` work. ``` use std::slice; -``` -``` - -``` - -``` fn split_at_mut( -``` - -``` values: &mut [i32], -``` - -``` mid: usize, -``` - -``` ) -> (&mut [i32], &mut [i32]) { -``` - -``` 1 let len = values.len(); -``` - -``` 2 let ptr = values.as_mut_ptr(); -``` -``` - -``` - -``` 3 assert!(mid <= len); -``` -``` - -``` - -``` 4 unsafe { -``` - -``` ( -``` - -``` 5 slice::from_raw_parts_mut(ptr, mid), -``` - -``` 6 slice::from_raw_parts_mut(ptr.add(mid), len - mid), -``` - -``` ) -``` - -``` } -``` - -``` } ``` -Using unsafe code in the implementation of the `split_at_mut` function +Listing 19-6: Using unsafe code in the implementation of the `split_at_mut` +function Recall from “The Slice Type” on page XX that a slice is a pointer to some data and the length of the slice. We use the `len` method to get the length of a @@ -577,37 +376,16 @@ location and creates a slice 10,000 items long. ``` use std::slice; -``` -``` - -``` - -``` let address = 0x01234usize; -``` - -``` let r = address as *mut i32; -``` -``` - -``` - -``` let values: &[i32] = unsafe { -``` - -``` slice::from_raw_parts_mut(r, 10000) -``` - -``` }; ``` -Creating a slice from an arbitrary memory location +Listing 19-7: Creating a slice from an arbitrary memory location We don’t own the memory at this arbitrary location, and there is no guarantee that the slice this code creates contains valid `i32` values. Attempting to use @@ -631,53 +409,21 @@ Filename: src/main.rs ``` extern "C" { -``` - -``` fn abs(input: i32) -> i32; -``` - -``` } -``` -``` - -``` - -``` fn main() { -``` - -``` unsafe { -``` - -``` println!( -``` - -``` "Absolute value of -3 according to C: {}", -``` - -``` abs(-3) -``` - -``` ); -``` - -``` } -``` - -``` } ``` -Declaring and calling an `extern` function defined in another language +Listing 19-8: Declaring and calling an `extern` function defined in another +language Within the `extern "C"` block, we list the names and signatures of external functions from another language we want to call. The `"C"` part defines which @@ -685,12 +431,8 @@ functions from another language we want to call. The `"C"` part defines which defines how to call the function at the assembly level. The `"C"` ABI is the most common and follows the C programming language’s ABI. - -Unmatched: BoxType - > ### Calling Rust Functions from Other Languages - - +> > We can also use `extern` to create an interface that allows other languages to call Rust functions. Instead of creating a whole `extern` block, we add the `extern` keyword and specify the ABI to use just before the `fn` keyword for @@ -701,20 +443,17 @@ contains more information for other parts of the compilation process to consume but is less human readable. Every programming language compiler mangles names slightly differently, so for a Rust function to be nameable by other languages, we must disable the Rust compiler’s name mangling. - - +> > In the following example, we make the `call_from_c` function accessible from C code, after it’s compiled to a shared library and linked from C: - - -Unmatched: BoxCode - -Unmatched: BoxCode - -Unmatched: BoxCode - -Unmatched: BoxCode - +> +> ``` +> #[no_mangle] +> pub extern "C" fn call_from_c() { +> println!("Just called a Rust function from C!"); +> } +> ``` +> > This usage of `extern` does not require `unsafe`. ### Accessing or Modifying a Mutable Static Variable @@ -730,25 +469,13 @@ Filename: src/main.rs ``` static HELLO_WORLD: &str = "Hello, world!"; -``` -``` - -``` - -``` fn main() { -``` - -``` println!("value is: {HELLO_WORLD}"); -``` - -``` } ``` -Defining and using an immutable static variable +Listing 19-9: Defining and using an immutable static variable Static variables are similar to constants, which we discussed in “Constants” on page XX. The names of static variables are in `SCREAMING_SNAKE_CASE` by @@ -769,65 +496,23 @@ Filename: src/main.rs ``` static mut COUNTER: u32 = 0; -``` -``` - -``` - -``` fn add_to_count(inc: u32) { -``` - -``` unsafe { -``` - -``` COUNTER += inc; -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` fn main() { -``` - -``` add_to_count(3); -``` -``` - -``` - -``` unsafe { -``` - -``` println!("COUNTER: {COUNTER}"); -``` - -``` } -``` - -``` } ``` -Reading from or writing to a mutable static variable is unsafe. +Listing 19-10: Reading from or writing to a mutable static variable is unsafe. As with regular variables, we specify mutability using the `mut` keyword. Any code that reads or writes from `COUNTER` must be within an `unsafe` block. This @@ -851,38 +536,20 @@ Listing 19-11. ``` unsafe trait Foo { -``` - -``` // methods go here -``` - -``` } -``` -``` - -``` - -``` unsafe impl Foo for i32 { -``` - -``` // method implementations go here -``` - -``` } ``` -Defining and implementing an unsafe trait +Listing 19-11: Defining and implementing an unsafe trait By using `unsafe impl`, we’re promising that we’ll uphold the invariants that the compiler can’t verify. -As an example, recall the `S``end` and `S``ync` marker traits we discussed in +As an example, recall the `Send` and `Sync` marker traits we discussed in “Extensible Concurrency with the Send and Sync Traits” on page XX: 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 @@ -938,25 +605,14 @@ iterating over. The definition of the `Iterator` trait is as shown in Listing ``` pub trait Iterator { -``` - -``` type Item; -``` -``` - -``` - -``` fn next(&mut self) -> Option; -``` - -``` } ``` -The definition of the `Iterator` trait that has an associated type `Item` +Listing 19-12: The definition of the `Iterator` trait that has an associated +type `Item` The type `Item` is a placeholder, and the `next` method’s definition shows that it will return values of type `Option`. Implementors of the @@ -973,21 +629,9 @@ Filename: src/lib.rs ``` impl Iterator for Counter { -``` - -``` type Item = u32; -``` -``` - -``` - -``` fn next(&mut self) -> Option { -``` - -``` --snip-- ``` @@ -996,17 +640,11 @@ This syntax seems comparable to that of generics. So why not just define the ``` pub trait Iterator { -``` - -``` fn next(&mut self) -> Option; -``` - -``` } ``` -A hypothetical definition of the `Iterator` trait using generics +Listing 19-13: A hypothetical definition of the `Iterator` trait using generics The difference is that when using generics, as in Listing 19-13, we must annotate the types in each implementation; because we can also implement @@ -1034,7 +672,7 @@ and documenting the associated type in the API documentation is a good practice. When we use generic type parameters, we can specify a default concrete type for the generic type. This eliminates the need for implementors of the trait to specify a concrete type if the default type works. You specify a default type -when declaring a generic type with the `<``PlaceholderType=ConcreteType``>` +when declaring a generic type with the `<`PlaceholderType`=`ConcreteType`>` syntax. A great example of a situation where this technique is useful is with *operator @@ -1052,105 +690,34 @@ Filename: src/main.rs ``` use std::ops::Add; -``` -``` - -``` - -``` #[derive(Debug, Copy, Clone, PartialEq)] -``` - -``` struct Point { -``` - -``` x: i32, -``` - -``` y: i32, -``` - -``` } -``` -``` - -``` - -``` impl Add for Point { -``` - -``` type Output = Point; -``` -``` - -``` - -``` fn add(self, other: Point) -> Point { -``` - -``` Point { -``` - -``` x: self.x + other.x, -``` - -``` y: self.y + other.y, -``` - -``` } -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` fn main() { -``` - -``` assert_eq!( -``` - -``` Point { x: 1, y: 0 } + Point { x: 2, y: 3 }, -``` - -``` Point { x: 3, y: 3 } -``` - -``` ); -``` - -``` } ``` -Implementing the `Add` trait to overload the `+` operator for `Point` instances +Listing 19-14: Implementing the `Add` trait to overload the `+` operator for +`Point` instances The `add` method adds the `x` values of two `Point` instances and the `y` values of two `Point` instances to create a new `Point`. The `Add` trait has an @@ -1162,21 +729,9 @@ definition: ``` trait Add { -``` - -``` type Output; -``` -``` - -``` - -``` fn add(self, rhs: Rhs) -> Self::Output; -``` - -``` } ``` @@ -1205,53 +760,21 @@ Filename: src/lib.rs ``` use std::ops::Add; -``` -``` - -``` - -``` struct Millimeters(u32); -``` - -``` struct Meters(u32); -``` -``` - -``` - -``` impl Add for Millimeters { -``` - -``` type Output = Millimeters; -``` -``` - -``` - -``` fn add(self, other: Meters) -> Millimeters { -``` - -``` Millimeters(self.0 + (other.0 * 1000)) -``` - -``` } -``` - -``` } ``` -Implementing the `Add` trait on `Millimeters` to add `Millimeters` and `Meters` +Listing 19-15: Implementing the `Add` trait on `Millimeters` to add +`Millimeters` and `Meters` To add `Millimeters` and `Meters`, we specify `impl Add` to set the value of the `Rhs` type parameter instead of using the default of `Self`. @@ -1289,114 +812,37 @@ Filename: src/main.rs ``` trait Pilot { -``` - -``` fn fly(&self); -``` - -``` } -``` -``` - -``` - -``` trait Wizard { -``` - -``` fn fly(&self); -``` - -``` } -``` -``` - -``` - -``` struct Human; -``` -``` - -``` - -``` impl Pilot for Human { -``` - -``` fn fly(&self) { -``` - -``` println!("This is your captain speaking."); -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` impl Wizard for Human { -``` - -``` fn fly(&self) { -``` - -``` println!("Up!"); -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` impl Human { -``` - -``` fn fly(&self) { -``` - -``` println!("*waving arms furiously*"); -``` - -``` } -``` - -``` } ``` -Two traits are defined to have a `fly` method and are implemented on the -`Human` type, and a `fly` method is implemented on `Human` directly. +Listing 19-16: Two traits are defined to have a `fly` method and are +implemented on the `Human` type, and a `fly` method is implemented on `Human` +directly. When we call `fly` on an instance of `Human`, the compiler defaults to calling the method that is directly implemented on the type, as shown in Listing 19-17. @@ -1405,21 +851,12 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let person = Human; -``` - -``` person.fly(); -``` - -``` } ``` -Calling `fly` on an instance of `Human` +Listing 19-17: Calling `fly` on an instance of `Human` Running this code will print `*waving arms furiously*`, showing that Rust called the `fly` method implemented on `Human` directly. @@ -1432,29 +869,14 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let person = Human; -``` - -``` Pilot::fly(&person); -``` - -``` Wizard::fly(&person); -``` - -``` person.fly(); -``` - -``` } ``` -Specifying which trait’s `fly` method we want to call +Listing 19-18: Specifying which trait’s `fly` method we want to call Specifying the trait name before the method name clarifies to Rust which implementation of `fly` we want to call. We could also write @@ -1466,13 +888,7 @@ Running this code prints the following: ``` This is your captain speaking. -``` - -``` Up! -``` - -``` *waving arms furiously* ``` @@ -1493,90 +909,30 @@ Filename: src/main.rs ``` trait Animal { -``` - -``` fn baby_name() -> String; -``` - -``` } -``` -``` - -``` - -``` struct Dog; -``` -``` - -``` - -``` impl Dog { -``` - -``` fn baby_name() -> String { -``` - -``` String::from("Spot") -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` impl Animal for Dog { -``` - -``` fn baby_name() -> String { -``` - -``` String::from("puppy") -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` fn main() { -``` - -``` println!("A baby dog is called a {}", Dog::baby_name()); -``` - -``` } ``` -A trait with an associated function and a type with an associated function of -the same name that also implements the trait +Listing 19-19: A trait with an associated function and a type with an +associated function of the same name that also implements the trait We implement the code for naming all puppies Spot in the `baby_name` associated function that is defined on `Dog`. The `Dog` type also implements the trait @@ -1601,18 +957,12 @@ Filename: src/main.rs ``` fn main() { -``` - -``` println!("A baby dog is called a {}", Animal::baby_name()); -``` - -``` } ``` -Attempting to call the `baby_name` function from the `Animal` trait, but Rust -doesn’t know which implementation to use +Listing 19-20: Attempting to call the `baby_name` function from the `Animal` +trait, but Rust doesn’t know which implementation to use Because `Animal::baby_name` doesn’t have a `self` parameter, and there could be other types that implement the `Animal` trait, Rust can’t figure out which @@ -1620,33 +970,12 @@ implementation of `Animal::baby_name` we want. We’ll get this compiler error: ``` error[E0283]: type annotations needed -``` - -``` --> src/main.rs:20:43 -``` - -``` | -``` - -``` 20 | println!("A baby dog is called a {}", Animal::baby_name()); -``` - -``` | ^^^^^^^^^^^^^^^^^ cannot infer -``` - -``` type -``` - -``` | -``` - -``` = note: cannot satisfy `_: Animal` ``` @@ -1659,30 +988,15 @@ Filename: src/main.rs ``` fn main() { -``` - -``` println!( -``` - -``` "A baby dog is called a {}", -``` - -``` ::baby_name() -``` - -``` ); -``` - -``` } ``` -Using fully qualified syntax to specify that we want to call the `baby_name` -function from the `Animal` trait as implemented on `Dog` +Listing 19-21: Using fully qualified syntax to specify that we want to call the +`baby_name` function from the `Animal` trait as implemented on `Dog` We’re providing Rust with a type annotation within the angle brackets, which indicates we want to call the `baby_name` method from the `Animal` trait as @@ -1724,21 +1038,9 @@ should print the following: ``` ********** -``` - -``` * * -``` - -``` * (1, 3) * -``` - -``` * * -``` - -``` ********** ``` @@ -1754,58 +1056,22 @@ Filename: src/main.rs ``` use std::fmt; -``` -``` - -``` - -``` trait OutlinePrint: fmt::Display { -``` - -``` fn outline_print(&self) { -``` - -``` let output = self.to_string(); -``` - -``` let len = output.len(); -``` - -``` println!("{}", "*".repeat(len + 4)); -``` - -``` println!("*{}*", " ".repeat(len + 2)); -``` - -``` println!("* {} *", output); -``` - -``` println!("*{}*", " ".repeat(len + 2)); -``` - -``` println!("{}", "*".repeat(len + 4)); -``` - -``` } -``` - -``` } ``` -Implementing the `OutlinePrint` trait that requires the functionality from -`Display` +Listing 19-22: Implementing the `OutlinePrint` trait that requires the +functionality from `Display` Because we’ve specified that `OutlinePrint` requires the `Display` trait, we can use the `to_string` function that is automatically implemented for any type @@ -1821,25 +1087,10 @@ Filename: src/main.rs ``` struct Point { -``` - -``` x: i32, -``` - -``` y: i32, -``` - -``` } -``` -``` - -``` - -``` impl OutlinePrint for Point {} ``` @@ -1847,57 +1098,18 @@ We get an error saying that `Display` is required but not implemented: ``` error[E0277]: `Point` doesn't implement `std::fmt::Display` -``` - -``` --> src/main.rs:20:6 -``` - -``` | -``` - -``` 20 | impl OutlinePrint for Point {} -``` - -``` | ^^^^^^^^^^^^ `Point` cannot be formatted with the default formatter -``` - -``` | -``` - -``` = help: the trait `std::fmt::Display` is not implemented for `Point` -``` - -``` = note: in format strings you may be able to use `{:?}` (or {:#?} for -``` - -``` pretty-print) instead -``` - -``` note: required by a bound in `OutlinePrint` -``` - -``` --> src/main.rs:3:21 -``` - -``` | -``` - -``` 3 | trait OutlinePrint: fmt::Display { -``` - -``` | ^^^^^^^^^^^^ required by this bound in `OutlinePrint` ``` @@ -1908,29 +1120,11 @@ Filename: src/main.rs ``` use std::fmt; -``` -``` - -``` - -``` impl fmt::Display for Point { -``` - -``` fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -``` - -``` write!(f, "({}, {})", self.x, self.y) -``` - -``` } -``` - -``` } ``` @@ -1962,73 +1156,26 @@ Filename: src/main.rs ``` use std::fmt; -``` -``` - -``` - -``` struct Wrapper(Vec); -``` -``` - -``` - -``` impl fmt::Display for Wrapper { -``` - -``` fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -``` - -``` write!(f, "[{}]", self.0.join(", ")) -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` fn main() { -``` - -``` let w = Wrapper(vec![ -``` - -``` String::from("hello"), -``` - -``` String::from("world"), -``` - -``` ]); -``` - -``` println!("w = {w}"); -``` - -``` } ``` -Creating a `Wrapper` type around `Vec` to implement `Display` +Listing 19-23: Creating a `Wrapper` type around `Vec` to implement +`Display` The implementation of `Display` uses `self.0` to access the inner `Vec` because `Wrapper` is a tuple struct and `Vec` is the item at index 0 in the @@ -2059,7 +1206,7 @@ the `!` type and dynamically sized types. ### Using the Newtype Pattern for Type Safety and Abstraction -> NoteThis section assumes you’ve read the earlier section “Using the Newtype +> Note: This section assumes you’ve read the earlier section “Using the Newtype Pattern to Implement External Traits” on page XX. The newtype pattern is also useful for tasks beyond those we’ve discussed so @@ -2101,25 +1248,10 @@ values of type `i32`: ``` type Kilometers = i32; -``` -``` - -``` - -``` let x: i32 = 5; -``` - -``` let y: Kilometers = 5; -``` -``` - -``` - -``` println!("x + y = {}", x + y); ``` @@ -2143,49 +1275,19 @@ code like that in Listing 19-24. ``` let f: Box = Box::new(|| { -``` - -``` println!("hi"); -``` - -``` }); -``` -``` - -``` - -``` fn takes_long_type(f: Box) { -``` - -``` --snip-- -``` - -``` } -``` -``` - -``` - -``` fn returns_long_type() -> Box { -``` - -``` --snip-- -``` - -``` } ``` -Using a long type in many places +Listing 19-24: Using a long type in many places A type alias makes this code more manageable by reducing the repetition. In Listing 19-25, we’ve introduced an alias named `Thunk` for the verbose type and @@ -2193,49 +1295,19 @@ can replace all uses of the type with the shorter alias `Thunk`. ``` type Thunk = Box; -``` -``` - -``` - -``` let f: Thunk = Box::new(|| println!("hi")); -``` -``` - -``` - -``` fn takes_long_type(f: Thunk) { -``` - -``` --snip-- -``` - -``` } -``` -``` - -``` - -``` fn returns_long_type() -> Thunk { -``` - -``` --snip-- -``` - -``` } ``` -Introducing a type alias `Thunk` to reduce repetition +Listing 19-25: Introducing a type alias `Thunk` to reduce repetition This code is much easier to read and write! Choosing a meaningful name for a type alias can help communicate your intent as well (*thunk* is a word for code @@ -2252,53 +1324,17 @@ the `Write` trait: ``` use std::fmt; -``` - -``` use std::io::Error; -``` -``` - -``` - -``` pub trait Write { -``` - -``` fn write(&mut self, buf: &[u8]) -> Result; -``` - -``` fn flush(&mut self) -> Result<(), Error>; -``` -``` - -``` - -``` fn write_all(&mut self, buf: &[u8]) -> Result<(), Error>; -``` - -``` fn write_fmt( -``` - -``` &mut self, -``` - -``` fmt: fmt::Arguments, -``` - -``` ) -> Result<(), Error>; -``` - -``` } ``` @@ -2316,29 +1352,11 @@ looking like this: ``` pub trait Write { -``` - -``` fn write(&mut self, buf: &[u8]) -> Result; -``` - -``` fn flush(&mut self) -> Result<()>; -``` -``` - -``` - -``` fn write_all(&mut self, buf: &[u8]) -> Result<()>; -``` - -``` fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<()>; -``` - -``` } ``` @@ -2356,13 +1374,7 @@ return. Here is an example: ``` fn bar() -> ! { -``` - -``` --snip-- -``` - -``` } ``` @@ -2376,21 +1388,12 @@ here in Listing 19-26. ``` let guess: u32 = match guess.trim().parse() { -``` - -``` Ok(num) => num, -``` - -``` Err(_) => continue, -``` - -``` }; ``` -A `match` with an arm that ends in `continue` +Listing 19-26: A `match` with an arm that ends in `continue` At the time, we skipped over some details in this code. In “The match Control Flow Construct” on page XX, we discussed that `match` arms must all return the @@ -2398,17 +1401,8 @@ same type. So, for example, the following code doesn’t work: ``` let guess = match guess.trim().parse() { -``` - -``` Ok(_) => 5, -``` - -``` Err(_) => "hello", -``` - -``` }; ``` @@ -2434,41 +1428,14 @@ this definition: ``` impl Option { -``` - -``` pub fn unwrap(self) -> T { -``` - -``` match self { -``` - -``` Some(val) => val, -``` - -``` None => panic!( -``` - -``` "called `Option::unwrap()` on a `None` value" -``` - -``` ), -``` - -``` } -``` - -``` } -``` - -``` } ``` @@ -2482,21 +1449,9 @@ One final expression that has the type `!` is a `loop`: ``` print!("forever "); -``` -``` - -``` - -``` loop { -``` - -``` print!("and ever "); -``` - -``` } ``` @@ -2520,9 +1475,6 @@ we can’t create a variable of type `str`, nor can we take an argument of type ``` let s1: str = "Hello there!"; -``` - -``` let s2: str = "How's it going?"; ``` @@ -2562,27 +1514,15 @@ generic function definition like this: ``` fn generic(t: T) { -``` - -``` --snip-- -``` - -``` } ``` +is actually treated as though we had written this: -Unmatched: BodyContinued - ``` +``` fn generic(t: T) { -``` - -``` --snip-- -``` - -``` } ``` @@ -2592,13 +1532,7 @@ restriction: ``` fn generic(t: &T) { -``` - -``` --snip-- -``` - -``` } ``` @@ -2641,57 +1575,21 @@ Filename: src/main.rs ``` fn add_one(x: i32) -> i32 { -``` - -``` x + 1 -``` - -``` } -``` -``` - -``` - -``` fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { -``` - -``` f(arg) + f(arg) -``` - -``` } -``` -``` - -``` - -``` fn main() { -``` - -``` let answer = do_twice(add_one, 5); -``` -``` - -``` - -``` println!("The answer is: {answer}"); -``` - -``` } ``` -Using the `fn` type to accept a function pointer as an argument +Listing 19-27: Using the `fn` type to accept a function pointer as an argument This code prints `The answer is: 12`. We specify that the parameter `f` in `do_twice` is an `fn` that takes one parameter of type `i32` and returns an @@ -2719,21 +1617,9 @@ numbers into a vector of strings, we could use a closure, like this: ``` let list_of_numbers = vec![1, 2, 3]; -``` - -``` let list_of_strings: Vec = list_of_numbers -``` - -``` .iter() -``` - -``` .map(|i| i.to_string()) -``` - -``` .collect(); ``` @@ -2742,21 +1628,9 @@ like this: ``` let list_of_numbers = vec![1, 2, 3]; -``` - -``` let list_of_strings: Vec = list_of_numbers -``` - -``` .iter() -``` - -``` .map(ToString::to_string) -``` - -``` .collect(); ``` @@ -2776,33 +1650,12 @@ closures, like so: ``` enum Status { -``` - -``` Value(u32), -``` - -``` Stop, -``` - -``` } -``` -``` - -``` - -``` let list_of_statuses: Vec = (0u32..20) -``` - -``` .map(Status::Value) -``` - -``` .collect(); ``` @@ -2824,13 +1677,7 @@ The following code tries to return a closure directly, but it won’t compile: ``` fn returns_closure() -> dyn Fn(i32) -> i32 { -``` - -``` |x| x + 1 -``` - -``` } ``` @@ -2838,61 +1685,19 @@ The compiler error is as follows: ``` error[E0746]: return type cannot have an unboxed trait object -``` - -``` --> src/lib.rs:1:25 -``` - -``` | -``` - -``` 1 | fn returns_closure() -> dyn Fn(i32) -> i32 { -``` - -``` | ^^^^^^^^^^^^^^^^^^ doesn't have a size known at -``` - -``` compile-time -``` - -``` | -``` - -``` = note: for information on `impl Trait`, see -``` - -``` -``` - -``` help: use `impl Fn(i32) -> i32` as the return type, as all return paths are of -``` - -``` type `[closure@src/lib.rs:2:5: 2:14]`, which implements `Fn(i32) -> i32` -``` - -``` | -``` - -``` 1 | fn returns_closure() -> impl Fn(i32) -> i32 { -``` - -``` | ~~~~~~~~~~~~~~~~~~~ ``` @@ -2902,13 +1707,7 @@ We can use a trait object: ``` fn returns_closure() -> Box i32> { -``` - -``` Box::new(|x| x + 1) -``` - -``` } ``` @@ -2929,6 +1728,7 @@ used on structs and enums * Attribute-like macros that define custom attributes usable on any item * Function-like macros that look like function calls but operate on the tokens specified as their argument + We’ll talk about each of these in turn, but first, let’s look at why we even need macros when we already have functions. @@ -2997,55 +1797,22 @@ Filename: src/lib.rs ``` 1 #[macro_export] -``` - -``` 2 macro_rules! vec { -``` - -``` 3 ( $( $x:expr ),* ) => { -``` - -``` { -``` - -``` let mut temp_vec = Vec::new(); -``` - -``` 4 $( -``` - -``` 5 temp_vec.push(6 $x); -``` - -``` )* -``` - -``` 7 temp_vec -``` - -``` } -``` - -``` }; -``` - -``` } ``` -A simplified version of the `vec!` macro definition +Listing 19-28: A simplified version of the `vec!` macro definition -> NoteThe actual definition of the `vec!` macro in the standard library +> Note: The actual definition of the `vec!` macro in the standard library includes code to pre-allocate the correct amount of memory up front. That code is an optimization that we don’t include here, to make the example simpler. @@ -3087,7 +1854,7 @@ When we call this macro with `vec![1, 2, 3];`, the `$x` pattern matches three times with the three expressions `1`, `2`, and `3`. Now let’s look at the pattern in the body of the code associated with this arm: -`temp_vec.push()` [5] within `$()*` at [4] and [7] is generated for each part +`temp_vec.push()` [5] within `$()* at [4] and [7] is generated for each part that matches `$()` in the pattern zero or more times depending on how many times the pattern matches. The `$x` [6] is replaced with each expression matched. When we call this macro with `vec![1, 2, 3];`, the code generated that @@ -3095,29 +1862,11 @@ replaces this macro call will be the following: ``` { -``` - -``` let mut temp_vec = Vec::new(); -``` - -``` temp_vec.push(1); -``` - -``` temp_vec.push(2); -``` - -``` temp_vec.push(3); -``` - -``` temp_vec -``` - -``` } ``` @@ -3148,25 +1897,13 @@ Filename: src/lib.rs ``` use proc_macro::TokenStream; -``` -``` - -``` - -``` #[some_attribute] -``` - -``` pub fn some_name(input: TokenStream) -> TokenStream { -``` - -``` } ``` -An example of defining a procedural macro +Listing 19-29: An example of defining a procedural macro The function that defines a procedural macro takes a `TokenStream` as an input and produces a `TokenStream` as an output. The `TokenStream` type is defined by @@ -3197,42 +1934,18 @@ Filename: src/main.rs ``` use hello_macro::HelloMacro; -``` - -``` use hello_macro_derive::HelloMacro; -``` -``` - -``` - -``` #[derive(HelloMacro)] -``` - -``` struct Pancakes; -``` -``` - -``` - -``` fn main() { -``` - -``` Pancakes::hello_macro(); -``` - -``` } ``` -The code a user of our crate will be able to write when using our procedural -macro +Listing 19-30: The code a user of our crate will be able to write when using +our procedural macro This code will print `Hello, Macro! My name is Pancakes!` when we’re done. The first step is to make a new library crate, like this: @@ -3247,13 +1960,7 @@ Filename: src/lib.rs ``` pub trait HelloMacro { -``` - -``` fn hello_macro(); -``` - -``` } ``` @@ -3262,53 +1969,17 @@ the trait to achieve the desired functionality, like so: ``` use hello_macro::HelloMacro; -``` -``` - -``` - -``` struct Pancakes; -``` -``` - -``` - -``` impl HelloMacro for Pancakes { -``` - -``` fn hello_macro() { -``` - -``` println!("Hello, Macro! My name is Pancakes!"); -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` fn main() { -``` - -``` Pancakes::hello_macro(); -``` - -``` } ``` @@ -3352,25 +2023,10 @@ Filename: hello_macro_derive/Cargo.toml ``` [lib] -``` - -``` proc-macro = true -``` -``` - -``` - -``` [dependencies] -``` - -``` syn = "1.0" -``` - -``` quote = "1.0" ``` @@ -3382,58 +2038,22 @@ Filename: hello_macro_derive/src/lib.rs ``` use proc_macro::TokenStream; -``` - -``` use quote::quote; -``` - -``` use syn; -``` -``` - -``` - -``` #[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 -``` - -``` let ast = syn::parse(input).unwrap(); -``` -``` - -``` - -``` // Build the trait implementation -``` - -``` impl_hello_macro(&ast) -``` - -``` } ``` -Code that most procedural macro crates will require in order to process Rust -code +Listing 19-31: Code that most procedural macro crates will require in order to +process Rust code Notice that we’ve split the code into the `hello_macro_derive` function, which is responsible for parsing the `TokenStream`, and the `impl_hello_macro` @@ -3472,74 +2092,26 @@ struct we get from parsing the `struct Pancakes;` string. ``` DeriveInput { -``` - -``` --snip-- -``` -``` - -``` - -``` ident: Ident { -``` - -``` ident: "Pancakes", -``` - -``` span: #0 bytes(95..103) -``` - -``` }, -``` - -``` data: Struct( -``` - -``` DataStruct { -``` - -``` struct_token: Struct, -``` - -``` fields: Unit, -``` - -``` semi_token: Some( -``` - -``` Semi -``` - -``` ) -``` - -``` } -``` - -``` ) -``` - -``` } ``` -The `DeriveInput` instance we get when parsing the code that has the macro’s -attribute in Listing 19-30 +Listing 19-32: The `DeriveInput` instance we get when parsing the code that has +the macro’s attribute in Listing 19-30 The fields of this struct show that the Rust code we’ve parsed is a unit struct with the `ident` (*identifier*, meaning the name) of `Pancakes`. There are more @@ -3570,61 +2142,22 @@ Filename: hello_macro_derive/src/lib.rs ``` fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream { -``` - -``` let name = &ast.ident; -``` - -``` let gen = quote! { -``` - -``` impl HelloMacro for #name { -``` - -``` fn hello_macro() { -``` - -``` println!( -``` - -``` "Hello, Macro! My name is {}!", -``` - -``` stringify!(#name) -``` - -``` ); -``` - -``` } -``` - -``` } -``` - -``` }; -``` - -``` gen.into() -``` - -``` } ``` -Implementing the `HelloMacro` trait using the parsed Rust code +Listing 19-33: Implementing the `HelloMacro` trait using the parsed Rust code We get an `Ident` struct instance containing the name (identifier) of the annotated type using `ast.ident`. The struct in Listing 19-32 shows that when @@ -3671,13 +2204,7 @@ dependencies; if not, you can specify them as `path` dependencies as follows: ``` [dependencies] -``` - -``` hello_macro = { path = "../hello_macro" } -``` - -``` hello_macro_derive = { path = "../hello_macro/hello_macro_derive" } ``` @@ -3701,9 +2228,6 @@ named `route` that annotates functions when using a web application framework: ``` #[route(GET, "/")] -``` - -``` fn index() { ``` @@ -3712,21 +2236,9 @@ macro. The signature of the macro definition function would look like this: ``` #[proc_macro_attribute] -``` - -``` pub fn route( -``` - -``` attr: TokenStream, -``` - -``` item: TokenStream -``` - -``` ) -> TokenStream { ``` @@ -3761,9 +2273,6 @@ syntactically correct, which is much more complex processing than a ``` #[proc_macro] -``` - -``` pub fn sql(input: TokenStream) -> TokenStream { ``` diff --git a/nostarch/chapter20.md b/nostarch/chapter20.md index 49b1a060..26136da9 100644 --- a/nostarch/chapter20.md +++ b/nostarch/chapter20.md @@ -16,11 +16,9 @@ lessons. For our final project, we’ll make a web server that says “hello” and looks like Figure 20-1 in a web browser. +Figure 20-1: Our final shared project -Unmatched: GraphicSlug - -Unmatched: CaptionLine - Here is our plan for building the web server: +Here is our plan for building the web server: 1. Learn a bit about TCP and HTTP. 1. Listen for TCP connections on a socket. @@ -30,7 +28,7 @@ Unmatched: CaptionLine Before we get started, we should mention one detail: the method we’ll use won’t be the best way to build a web server with Rust. Community members have published a number of production-ready crates available at *https://crates.io* - that provide more complete web server and thread pool implementations than +that provide more complete web server and thread pool implementations than we’ll build. However, our intention in this chapter is to help you learn, not to take the easy route. Because Rust is a systems programming language, we can choose the level of abstraction we want to work with and can go to a lower @@ -45,12 +43,11 @@ let’s look at a quick overview of the protocols involved in building web servers. The details of these protocols are beyond the scope of this book, but a brief overview will give you the information you need. -The two main protocols involved in web servers are *Hypertext* *Transfer* -*Protocol* *(HTTP)* and *Transmission* *Control* *Protocol* *(TCP)*. Both -protocols are *request-response* protocols, meaning a *client* initiates -requests and a *server* listens to the requests and provides a response to the -client. The contents of those requests and responses are defined by the -protocols. +The two main protocols involved in web servers are *Hypertext Transfer +Protocol* *(HTTP)* and *Transmission Control Protocol* *(TCP)*. Both protocols +are *request-response* protocols, meaning a *client* initiates requests and a +*server* listens to the requests and provides a response to the client. The +contents of those requests and responses are defined by the protocols. TCP is the lower-level protocol that describes the details of how information gets from one server to another but doesn’t specify what that information is. @@ -67,67 +64,32 @@ this. Let’s make a new project in the usual fashion: ``` $ cargo new hello -``` - -``` Created binary (application) `hello` project -``` - -``` $ cd hello ``` Now enter the code in Listing 20-1 in *src/main.rs* to start. This code will listen at the local address `127.0.0.1:7878` for incoming TCP streams. When it -gets an incoming stream, it will print `Connection` `established!`. +gets an incoming stream, it will print `Connection established!`. Filename: src/main.rs ``` use std::net::TcpListener; -``` -``` - -``` - -``` fn main() { -``` - -``` 1 let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); -``` -``` - -``` - -``` 2 for stream in listener.incoming() { -``` - -``` 3 let stream = stream.unwrap(); -``` -``` - -``` - -``` 4 println!("Connection established!"); -``` - -``` } -``` - -``` } ``` -Listening for incoming streams and printing a message when we receive a stream +Listing 20-1: Listening for incoming streams and printing a message when we +receive a stream Using `TcpListener`, we can listen for TCP connections at the address `127.0.0.1:7878` [1]. In the address, the section before the colon is an IP @@ -142,7 +104,7 @@ will return a new `TcpListener` instance. The function is called `bind` because, in networking, connecting to a port to listen to is known as “binding to a port.” -The `bind` function returns a `Result`, which indicates that it’s +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 (non-administrators can listen only on ports higher than 1023), so if we tried to connect to port 80 without being an @@ -167,14 +129,14 @@ our program if the stream has any errors [3]; if there aren’t any errors, the program prints a message [4]. We’ll add more functionality for the success case in the next listing. The reason we might receive errors from the `incoming` method when a client connects to the server is that we’re not actually -iterating over connections. Instead, we’re iterating over *connection* -*attempts*. The connection might not be successful for a number of reasons, -many of them operating system specific. For example, many operating systems -have a limit to the number of simultaneous open connections they can support; -new connection attempts beyond that number will produce an error until some of -the open connections are closed. +iterating over connections. Instead, we’re iterating over *connection +attempts*. The connection might not be successful for a number of reasons, many +of them operating system specific. For example, many operating systems have a +limit to the number of simultaneous open connections they can support; new +connection attempts beyond that number will produce an error until some of the +open connections are closed. -Let’s try running this code! Invoke `cargo` `run` in the terminal and then load +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 data. But when you look at your terminal, you should see several messages that @@ -182,17 +144,8 @@ were printed when the browser connected to the server! ``` Running `target/debug/hello` -``` - -``` Connection established! -``` - -``` Connection established! -``` - -``` Connection established! ``` @@ -209,9 +162,9 @@ connections by retrying, because the problem might be temporary. The important factor is that we’ve successfully gotten a handle to a TCP connection! Remember to stop the program by pressing ctrl-C when you’re done running a -particular version of the code. Then restart the program by invoking the -`cargo` `run` command after you’ve made each set of code changes to make sure -you’re running the newest code. +particular version of the code. Then restart the program by invoking the `cargo +run` command after you’ve made each set of code changes to make sure you’re +running the newest code. ### Reading the Request @@ -226,105 +179,33 @@ Filename: src/main.rs ``` 1 use std::{ -``` - -``` io::{prelude::*, BufReader}, -``` - -``` net::{TcpListener, TcpStream}, -``` - -``` }; -``` -``` - -``` - -``` fn main() { -``` - -``` let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); -``` -``` - -``` - -``` for stream in listener.incoming() { -``` - -``` let stream = stream.unwrap(); -``` -``` - -``` - -``` 2 handle_connection(stream); -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` fn handle_connection(mut stream: TcpStream) { -``` - -``` 3 let buf_reader = BufReader::new(&mut stream); -``` - -``` 4 let http_request: Vec<_> = buf_reader -``` - -``` 5 .lines() -``` - -``` 6 .map(|result| result.unwrap()) -``` - -``` 7 .take_while(|line| !line.is_empty()) -``` - -``` .collect(); -``` -``` - -``` - -``` 8 println!("Request: {:#?}", http_request); -``` - -``` } ``` -Reading from the `TcpStream` and printing the data +Listing 20-2: Reading from the `TcpStream` and printing the data We bring `std::io::prelude` and `std::io::BufReader` into scope to get access to traits and types that let us read from and write to the stream [1]. In the @@ -341,8 +222,8 @@ the browser sends to our server. We indicate that we want to collect these lines in a vector by adding the `Vec<_>` type annotation [4]. `BufReader` implements the `std::io::BufRead` trait, which provides the `lines` -method [5]. The `lines` method returns an iterator of `Result` by splitting the stream of data whenever it sees a newline +method [5]. The `lines` method returns an iterator of `Result` by splitting the stream of data whenever it sees a newline byte. To get each `String`, we map and `unwrap` each `Result` [6]. The `Result` might be an error if the data isn’t valid UTF-8 or if there was a problem reading from the stream. Again, a production program should handle these errors @@ -361,93 +242,27 @@ program’s output in the terminal will now look similar to this: ``` $ cargo run -``` - -``` Compiling hello v0.1.0 (file:///projects/hello) -``` - -``` Finished dev [unoptimized + debuginfo] target(s) in 0.42s -``` - -``` Running `target/debug/hello` -``` - -``` Request: [ -``` - -``` "GET / HTTP/1.1", -``` - -``` "Host: 127.0.0.1:7878", -``` - -``` "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:99.0) -``` - -``` Gecko/20100101 Firefox/99.0", -``` - -``` "Accept: -``` - -``` text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/* -``` - -``` ;q=0.8", -``` - -``` "Accept-Language: en-US,en;q=0.5", -``` - -``` "Accept-Encoding: gzip, deflate, br", -``` - -``` "DNT: 1", -``` - -``` "Connection: keep-alive", -``` - -``` "Upgrade-Insecure-Requests: 1", -``` - -``` "Sec-Fetch-Dest: document", -``` - -``` "Sec-Fetch-Mode: navigate", -``` - -``` "Sec-Fetch-Site: none", -``` - -``` "Sec-Fetch-User: ?1", -``` - -``` "Cache-Control: max-age=0", -``` - -``` ] ``` @@ -467,34 +282,27 @@ HTTP is a text-based protocol, and a request takes this format: ``` Method Request-URI HTTP-Version CRLF -``` - -``` headers CRLF -``` - -``` message-body ``` -The first line is the *request* *line* that holds information about what the +The first line is the *request line* that holds information about what the client is requesting. The first part of the request line indicates the *method* 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 *u* *niform* *r* -*esource* *i* *dentifier* *(URI)* the client is requesting: a URI is almost, -but not quite, the same as a *u* *niform* *r* *esource* *l* *ocator* *(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. +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. 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*, 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. +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. 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, @@ -516,17 +324,11 @@ Responses have the following format: ``` HTTP-Version Status-Code Reason-Phrase CRLF -``` - -``` headers CRLF -``` - -``` message-body ``` -The first line is a *status* *line* that contains the HTTP version used in the +The first line is a *status line* that contains the HTTP version used in the response, a numeric status code that summarizes the result of the request, and 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 @@ -549,53 +351,20 @@ Filename: src/main.rs ``` fn handle_connection(mut stream: TcpStream) { -``` - -``` let buf_reader = BufReader::new(&mut stream); -``` - -``` let http_request: Vec<_> = buf_reader -``` - -``` .lines() -``` - -``` .map(|result| result.unwrap()) -``` - -``` .take_while(|line| !line.is_empty()) -``` - -``` .collect(); -``` -``` - -``` - -``` 1 let response = "HTTP/1.1 200 OK\r\n\r\n"; -``` -``` - -``` - -``` 2 stream.write_all(response.3 as_bytes()).unwrap(); -``` - -``` } ``` -Writing a tiny successful HTTP response to the stream +Listing 20-3: Writing a tiny successful HTTP response to the stream The first new line defines the `response` variable that holds the success message’s data [1]. Then we call `as_bytes` on our `response` to convert the @@ -621,49 +390,19 @@ Filename: hello.html ``` -``` - -``` -``` - -``` -``` - -``` -``` - -``` Hello! -``` - -``` -``` - -``` -``` - -```

Hello!

-``` - -```

Hi from Rust

-``` - -``` -``` - -``` ``` -A sample HTML file to return in a response +Listing 20-4: A sample HTML file to return in a response This is a minimal HTML5 document with a heading and some text. To return this from the server when a request is received, we’ll modify `handle_connection` as @@ -674,101 +413,35 @@ Filename: src/main.rs ``` use std::{ -``` - -``` 1 fs, -``` - -``` io::{prelude::*, BufReader}, -``` - -``` net::{TcpListener, TcpStream}, -``` - -``` }; -``` - -``` --snip-- -``` -``` - -``` - -``` fn handle_connection(mut stream: TcpStream) { -``` - -``` let buf_reader = BufReader::new(&mut stream); -``` - -``` let http_request: Vec<_> = buf_reader -``` - -``` .lines() -``` - -``` .map(|result| result.unwrap()) -``` - -``` .take_while(|line| !line.is_empty()) -``` - -``` .collect(); -``` -``` - -``` - -``` let status_line = "HTTP/1.1 200 OK"; -``` - -``` let contents = fs::read_to_string("hello.html").unwrap(); -``` - -``` let length = contents.len(); -``` -``` + 2 let response = format!( + "{status_line}\r\n\ + Content-Length: {length}\r\n\r\n\ + {contents}" + ); -``` - -``` - 2 let response = -``` - -``` - format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"); -``` - -``` - -``` - -``` stream.write_all(response.as_bytes()).unwrap(); -``` - -``` } ``` -Sending the contents of *hello.html* as the body of the response +Listing 20-5: Sending the contents of *hello.html* as the body of the response We’ve added `fs` to the `use` statement to bring the standard library’s filesystem module into scope [1]. The code for reading the contents of a file @@ -780,7 +453,7 @@ response [2]. To ensure a valid HTTP response, we add the `Content-Length` header which is set to the size of our response body, in this case the size of `hello.html`. -Run this code with `cargo` `run` and load *127.0.0.1:7878* in your browser; you +Run this code with `cargo run` and load *127.0.0.1:7878* in your browser; you should see your HTML rendered! Currently, we’re ignoring the request data in `http_request` and just sending @@ -805,109 +478,34 @@ Filename: src/main.rs ``` --snip-- -``` -``` - -``` - -``` fn handle_connection(mut stream: TcpStream) { -``` - -``` let buf_reader = BufReader::new(&mut stream); -``` - -``` 1 let request_line = buf_reader -``` + .lines() + .next() + .unwrap() + .unwrap(); -``` -.lines() -``` - -``` -.next() -``` - -``` -.unwrap() -``` - -``` -.unwrap(); -``` - -``` - -``` - -``` 2 if request_line == "GET / HTTP/1.1" { -``` - -``` let status_line = "HTTP/1.1 200 OK"; -``` - -``` let contents = fs::read_to_string("hello.html").unwrap(); -``` - -``` let length = contents.len(); -``` -``` - -``` - -``` let response = format!( -``` - -``` - "{status_line}\r\n -``` - -``` -Content-Length: {length}\r\n\r\n -``` - -``` -{contents}" -``` - -``` + "{status_line}\r\n\ + Content-Length: {length}\r\n\r\n\ + {contents}" ); -``` -``` - -``` - -``` stream.write_all(response.as_bytes()).unwrap(); -``` - -``` 3 } else { -``` - -``` // some other request -``` - -``` } -``` - -``` } ``` -Handling requests to */* differently from other requests +Listing 20-6: Handling requests to */* differently from other requests We’re only going to be looking at the first line of the HTTP request, so rather than reading the entire request into a vector, we’re calling `next` to get the @@ -938,65 +536,26 @@ Filename: src/main.rs ``` --snip-- -``` - -``` } else { -``` - -``` 1 let status_line = "HTTP/1.1 404 NOT FOUND"; -``` - -``` 2 let contents = fs::read_to_string("404.html").unwrap(); -``` - -``` let length = contents.len(); -``` -``` - -``` - -``` let response = format!( -``` - -``` - "{status_line}\r\n -``` - -``` -Content-Length: {length}\r\n\r\n -``` - -``` -{contents}" -``` - -``` + "{status_line}\r\n\ + Content-Length: {length}\r\n\r\n + {contents}" ); -``` -``` - -``` - -``` stream.write_all(response.as_bytes()).unwrap(); -``` - -``` } ``` -Responding with status code 404 and an error page if anything other than */* -was requested +Listing 20-7: Responding with status code 404 and an error page if anything +other than */* was requested Here, our response has a status line with status code 404 and the reason phrase -`NOT` `FOUND` [1]. The body of the response will be the HTML in the file +`NOT FOUND` [1]. The body of the response will be the HTML in the file *404.html* [1]. You’ll need to create a *404.html* file next to *hello.html* for the error page; again feel free to use any HTML you want, or use the example HTML in Listing 20-8. @@ -1005,49 +564,19 @@ Filename: 404.html ``` -``` - -``` -``` - -``` -``` - -``` -``` - -``` Hello! -``` - -``` -``` - -``` -``` - -```

Oops!

-``` - -```

Sorry, I don't know what you're asking for.

-``` - -``` -``` - -``` ``` -Sample content for the page to send back with any 404 response +Listing 20-8: Sample content for the page to send back with any 404 response With these changes, run your server again. Requesting *127.0.0.1:7878* should return the contents of *hello.html*, and any other request, like @@ -1068,102 +597,32 @@ Filename: src/main.rs ``` --snip-- -``` -``` - -``` - -``` fn handle_connection(mut stream: TcpStream) { -``` - -``` --snip-- -``` -``` - -``` - -``` let (status_line, filename) = -``` + if request_line == "GET / HTTP/1.1" { + ("HTTP/1.1 200 OK", "hello.html") + } else { + ("HTTP/1.1 404 NOT FOUND", "404.html") + }; -``` -if request_line == "GET / HTTP/1.1" { -``` - -``` - ("HTTP/1.1 200 OK", "hello.html") -``` - -``` - } else { -``` - -``` - ("HTTP/1.1 404 NOT FOUND", "404.html") -``` - -``` - }; -``` - -``` - -``` - -``` let contents = fs::read_to_string(filename).unwrap(); -``` - -``` let length = contents.len(); -``` -``` + let response = format!( + "{status_line}\r\n\ + Content-Length: {length}\r\n\r\n\ + {contents}" + ); -``` - -``` - let response = -``` - -``` -format!( -``` - -``` -"{status_line}\r\n -``` - -``` -Content-Length: {length}\r\n\r\n -``` - -``` -{contents}" -``` - -``` -); -``` - -``` - -``` - -``` stream.write_all(response.as_bytes()).unwrap(); -``` - -``` } ``` -Refactoring the `if` and `else` blocks to contain only the code that differs -between the two cases +Listing 20-9: Refactoring the `if` and `else` blocks to contain only the code +that differs between the two cases Now the `if` and `else` blocks only return the appropriate values for the status line and filename in a tuple; we then use destructuring to assign these @@ -1207,97 +666,31 @@ Filename: src/main.rs ``` use std::{ -``` - -``` fs, -``` - -``` io::{prelude::*, BufReader}, -``` - -``` net::{TcpListener, TcpStream}, -``` - -``` thread, -``` - -``` time::Duration, -``` - -``` }; -``` - -``` --snip-- -``` -``` - -``` - -``` fn handle_connection(mut stream: TcpStream) { -``` - -``` --snip-- -``` -``` - -``` - -``` let (status_line, filename) = 1 match &request_line[..] { -``` - -``` 2 "GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"), -``` - -``` 3 "GET /sleep HTTP/1.1" => { -``` - -``` thread::sleep(Duration::from_secs(5)); -``` - -``` ("HTTP/1.1 200 OK", "hello.html") -``` - -``` } -``` - -``` 4 _ => ("HTTP/1.1 404 NOT FOUND", "404.html"), -``` - -``` }; -``` -``` - -``` - -``` --snip-- -``` - -``` } ``` -Simulating a slow request by sleeping for five seconds +Listing 20-10: Simulating a slow request by sleeping for five seconds We switched from `if` to `match` now that we have three cases [1]. We need to explicitly match on a slice of `request_line` to pattern-match against the @@ -1312,18 +705,18 @@ The third arm [4] is the same as the `else` block from Listing 20-9. You can see how primitive our server is: real libraries would handle the recognition of multiple requests in a much less verbose way! -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 five seconds before loading. +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 five seconds before loading. There are multiple techniques we could use to avoid requests backing up behind a slow request; the one we’ll implement is a thread pool. ### Improving Throughput with a Thread Pool -A *thread* *pool* is a group of spawned threads that are waiting and ready to +A *thread pool* is a group of spawned threads that are waiting and ready to handle a task. When the program receives a new task, it assigns one of the threads in the pool to the task, and that thread will process the task. The remaining threads in the pool are available to handle any other tasks that come @@ -1343,17 +736,17 @@ 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 -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. +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*, 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. +server. Other options you might explore are the fork/join model, the +single-threaded async I/O model, and the multithreaded 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. Before we begin implementing a thread pool, let’s talk about what using the pool should look like. When you’re trying to design code, writing the client @@ -1383,49 +776,19 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); -``` -``` - -``` - -``` for stream in listener.incoming() { -``` - -``` let stream = stream.unwrap(); -``` -``` - -``` - -``` thread::spawn(|| { -``` - -``` handle_connection(stream); -``` - -``` }); -``` - -``` } -``` - -``` } ``` -Spawning a new thread for each stream +Listing 20-11: Spawning a new thread for each stream As you learned in Chapter 16, `thread::spawn` will create a new thread and then run the code in the closure in the new thread. If you run this code and load @@ -1445,53 +808,20 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); -``` - -``` 1 let pool = ThreadPool::new(4); -``` -``` - -``` - -``` for stream in listener.incoming() { -``` - -``` let stream = stream.unwrap(); -``` -``` - -``` - -``` 2 pool.execute(|| { -``` - -``` handle_connection(stream); -``` - -``` }); -``` - -``` } -``` - -``` } ``` -Our ideal `ThreadPool` interface +Listing 20-12: Our ideal `ThreadPool` interface We use `ThreadPool::new` to create a new thread pool with a configurable number of threads, in this case four [1]. Then, in the `for` loop, `pool.execute` has @@ -1503,34 +833,16 @@ compile, but we’ll try so that the compiler can guide us in how to fix it. #### Building ThreadPool Using Compiler-Driven Development Make the changes in Listing 20-12 to *src/main.rs*, and then let’s use the -compiler errors from `cargo` `check` to drive our development. Here is the -first error we get: +compiler errors from `cargo check` to drive our development. Here is the first +error we get: ``` $ cargo check -``` - -``` Checking hello v0.1.0 (file:///projects/hello) -``` - -``` error[E0433]: failed to resolve: use of undeclared type `ThreadPool` -``` - -``` - --> src/main.rs:1:16 -``` - -``` + --> src/main.rs:11:16 | -``` - -``` -1 | let pool = ThreadPool::new(4); -``` - -``` +11 | let pool = ThreadPool::new(4); | ^^^^^^^^^^ use of undeclared type `ThreadPool` ``` @@ -1542,7 +854,7 @@ 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 an *src/lib.rs* file 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: Filename: src/lib.rs @@ -1565,37 +877,13 @@ we need to address: ``` $ cargo check -``` - -``` Checking hello v0.1.0 (file:///projects/hello) -``` - -``` error[E0599]: no function or associated item named `new` found for struct -``` - -``` `ThreadPool` in the current scope -``` - -``` - --> src/main.rs::28 -``` - -``` + --> src/main.rs:12:28 | -``` - -``` -1 | let pool = ThreadPool::new(4); -``` - -``` +12 | let pool = ThreadPool::new(4); | ^^^ function or associated item not found in -``` - -``` `ThreadPool` ``` @@ -1609,29 +897,11 @@ Filename: src/lib.rs ``` pub struct ThreadPool; -``` -``` - -``` - -``` impl ThreadPool { -``` - -``` pub fn new(size: usize) -> ThreadPool { -``` - -``` ThreadPool -``` - -``` } -``` - -``` } ``` @@ -1644,33 +914,12 @@ Let’s check the code again: ``` $ cargo check -``` - -``` Checking hello v0.1.0 (file:///projects/hello) -``` - -``` error[E0599]: no method named `execute` found for struct `ThreadPool` in the -``` - -``` current scope -``` - -``` - --> src/main.rs:1:14 -``` - -``` + --> src/main.rs:17:14 | -``` - -``` -1 | pool.execute(|| { -``` - -``` +17 | pool.execute(|| { | ^^^^^^^ method not found in `ThreadPool` ``` @@ -1690,21 +939,9 @@ use here. We know we’ll end up doing something similar to the standard library ``` pub fn spawn(f: F) -> JoinHandle -``` - -``` where -``` - -``` F: FnOnce() -> T, -``` - -``` F: Send + 'static, -``` - -``` T: Send + 'static, ``` @@ -1726,33 +963,12 @@ Filename: src/lib.rs ``` impl ThreadPool { -``` - -``` --snip-- -``` - -``` pub fn execute(&self, f: F) -``` - -``` where -``` - -``` F: FnOnce() 1 + Send + 'static, -``` - -``` { -``` - -``` } -``` - -``` } ``` @@ -1766,22 +982,16 @@ nothing, but we’re only trying to make our code compile. Let’s check it agai ``` $ cargo check -``` - -``` Checking hello v0.1.0 (file:///projects/hello) -``` - -``` Finished dev [unoptimized + debuginfo] target(s) in 0.24s ``` -It compiles! But note that if you try `cargo` `run` and make a request in the +It compiles! But note that if you try `cargo run` and make a request in the browser, you’ll see the errors in the browser that we saw at the beginning of the chapter. Our library isn’t actually calling the closure passed to `execute` yet! -> NoteA saying you might hear about languages with strict compilers, such as +> Note: A saying you might hear about languages with strict compilers, such as Haskell and Rust, is “if the code compiles, it works.” But this saying is not universally true. Our project compiles, but it does absolutely nothing! If we were building a real, complete project, this would be a good time to start @@ -1803,75 +1013,30 @@ Filename: src/lib.rs ``` impl ThreadPool { -``` - -``` /// Create a new ThreadPool. -``` - -``` /// -``` - -``` /// The size is the number of threads in the pool. -``` - -``` /// -``` - -``` 1 /// # Panics -``` - -``` /// -``` - -``` /// The `new` function will panic if the size is zero. -``` - -``` pub fn new(size: usize) -> ThreadPool { -``` - -``` 2 assert!(size > 0); -``` -``` - -``` - -``` ThreadPool -``` - -``` } -``` -``` - -``` - -``` --snip-- -``` - -``` } ``` -Implementing `ThreadPool::new` to panic if `size` is zero +Listing 20-13: Implementing `ThreadPool::new` to panic if `size` is zero We’ve also added some documentation for our `ThreadPool` with doc comments. Note that we followed good documentation practices by adding a section that calls out the situations in which our function can panic [1], as discussed in -Chapter 14. Try running `cargo` `doc` `--open` and clicking the `ThreadPool` -struct to see what the generated docs for `new` look like! +Chapter 14. Try running `cargo doc --open` and clicking the `ThreadPool` struct +to see what the generated docs for `new` look like! Instead of adding the `assert!` macro as we’ve done here [2], we could change `new` into `build` and return a `Result` like we did with `Config::build` in @@ -1882,13 +1047,7 @@ following signature to compare with the `new` function: ``` pub fn build( -``` - -``` -size: usize -``` - -``` + size: usize ) -> Result { ``` @@ -1901,21 +1060,9 @@ look at the `thread::spawn` signature: ``` pub fn spawn(f: F) -> JoinHandle -``` - -``` where -``` - -``` F: FnOnce() -> T, -``` - -``` F: Send + 'static, -``` - -``` T: Send + 'static, ``` @@ -1934,89 +1081,29 @@ Filename: src/lib.rs ``` 1 use std::thread; -``` -``` - -``` - -``` pub struct ThreadPool { -``` - -``` 2 threads: Vec>, -``` - -``` } -``` -``` - -``` - -``` impl ThreadPool { -``` - -``` --snip-- -``` - -``` pub fn new(size: usize) -> ThreadPool { -``` - -``` assert!(size > 0); -``` -``` - -``` - -``` 3 let mut threads = Vec::with_capacity(size); -``` -``` - -``` - -``` for _ in 0..size { -``` - -``` // create some threads and store them in the vector -``` - -``` } -``` -``` - -``` - -``` ThreadPool { threads } -``` - -``` } -``` - -``` --snip-- -``` - -``` } ``` -Creating a vector for `ThreadPool` to hold the threads +Listing 20-14: Creating a vector for `ThreadPool` to hold the threads We’ve brought `std::thread` into scope in the library crate [1] because we’re using `thread::JoinHandle` as the type of the items in the vector in @@ -2029,7 +1116,7 @@ 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. -When you run `cargo` `check` again, it should succeed. +When you run `cargo check` again, it should succeed. #### Sending Code from the ThreadPool to a Thread @@ -2045,8 +1132,8 @@ 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. +implementations. The `Worker` picks up code that needs to be run and runs the +code in its 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 @@ -2056,8 +1143,8 @@ 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` @@ -2069,7 +1156,7 @@ set up in this way: `Worker` instance that holds the `id` and a thread spawned with an empty closure. 1. In `ThreadPool::new`, use the `for` loop counter to generate an `id`, create -a new `Worker` with that `id`, and store the worker in the vector. +a new `Worker` with that `id`, and store the `Worker` in the vector. If you’re up for a challenge, try implementing these changes on your own before looking at the code in Listing 20-15. @@ -2079,142 +1166,43 @@ Filename: src/lib.rs ``` use std::thread; -``` -``` - -``` - -``` pub struct ThreadPool { -``` - -``` 1 workers: Vec, -``` - -``` } -``` -``` - -``` - -``` impl ThreadPool { -``` - -``` --snip-- -``` - -``` pub fn new(size: usize) -> ThreadPool { -``` - -``` assert!(size > 0); -``` -``` - -``` - -``` let mut workers = Vec::with_capacity(size); -``` -``` - -``` - -``` 2 for id in 0..size { -``` - -``` 3 workers.push(Worker::new(id)); -``` - -``` } -``` -``` - -``` - -``` ThreadPool { workers } -``` - -``` } -``` - -``` --snip-- -``` - -``` } -``` -``` - -``` - -``` 4 struct Worker { -``` - -``` id: usize, -``` - -``` thread: thread::JoinHandle<()>, -``` - -``` } -``` -``` - -``` - -``` impl Worker { -``` - -``` 5 fn new(id: usize) -> Worker { -``` - -``` 6 let thread = thread::spawn(|| {}); -``` -``` - -``` - -``` Worker { 7 id, 8 thread } -``` - -``` } -``` - -``` } ``` -Modifying `ThreadPool` to hold `Worker` instances instead of holding threads -directly +Listing 20-15: Modifying `ThreadPool` to hold `Worker` instances instead of +holding threads directly We’ve changed the name of the field on `ThreadPool` from `threads` to `workers` because it’s now holding `Worker` instances instead of `JoinHandle<()>` @@ -2228,9 +1216,9 @@ so we make the `Worker` struct [4] and its `new` function [5] private. The `JoinHandle<()>` instance [8] that is created by spawning a new thread using an empty closure [6]. -> NoteIf the operating system can’t create a thread because there aren’t enough -system resources, `thread::spawn` will panic. That will cause our whole server -to panic, even though the creation of some threads might succeed. For +> Note: If the operating system can’t create a thread because there aren’t +enough system resources, `thread::spawn` will panic. That will cause our whole +server to panic, even though the creation of some threads might succeed. For simplicity’s sake, this behavior is fine, but in a production thread pool implementation, you’d likely want to use `std::thread::Builder` and its `spawn` method that returns `Result` instead. @@ -2271,238 +1259,79 @@ Filename: src/lib.rs ``` use std::{sync::mpsc, thread}; -``` -``` - -``` - -``` pub struct ThreadPool { -``` - -``` workers: Vec, -``` - -``` sender: mpsc::Sender, -``` - -``` } -``` -``` - -``` - -``` struct Job; -``` -``` - -``` - -``` impl ThreadPool { -``` - -``` --snip-- -``` - -``` pub fn new(size: usize) -> ThreadPool { -``` - -``` assert!(size > 0); -``` -``` - -``` - -``` 1 let (sender, receiver) = mpsc::channel(); -``` -``` - -``` - -``` let mut workers = Vec::with_capacity(size); -``` -``` - -``` - -``` for id in 0..size { -``` - -``` workers.push(Worker::new(id)); -``` - -``` } -``` -``` - -``` - -``` ThreadPool { workers, 2 sender } -``` - -``` } -``` - -``` --snip-- -``` - -``` } ``` -Modifying `ThreadPool` to store the sender of a channel that transmits `Job` -instances +Listing 20-16: Modifying `ThreadPool` to store the sender of a channel that +transmits `Job` instances In `ThreadPool::new`, we create our new channel [1] and have the pool hold the sender [2]. 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 20-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 20-17 won’t quite compile yet. Filename: src/lib.rs ``` impl ThreadPool { -``` - -``` --snip-- -``` - -``` pub fn new(size: usize) -> ThreadPool { -``` - -``` assert!(size > 0); -``` -``` - -``` - -``` let (sender, receiver) = mpsc::channel(); -``` -``` - -``` - -``` let mut workers = Vec::with_capacity(size); -``` -``` - -``` - -``` for id in 0..size { -``` - -``` 1 workers.push(Worker::new(id, receiver)); -``` - -``` } -``` -``` - -``` - -``` ThreadPool { workers, sender } -``` - -``` } -``` - -``` --snip-- -``` - -``` } -``` -``` - -``` - -``` --snip-- -``` -``` - -``` - -``` impl Worker { -``` - -``` fn new(id: usize, receiver: mpsc::Receiver) -> Worker { -``` - -``` let thread = thread::spawn(|| { -``` - -``` 2 receiver; -``` - -``` }); -``` -``` - -``` - -``` Worker { id, thread } -``` - -``` } -``` - -``` } ``` -Passing the receiver to the workers +Listing 20-17: Passing the receiver to each `Worker` We’ve made some small and straightforward changes: we pass the receiver into `Worker::new` [1], and then we use it inside the closure [2]. @@ -2511,53 +1340,16 @@ When we try to check this code, we get this error: ``` $ cargo check -``` - -``` Checking hello v0.1.0 (file:///projects/hello) -``` - -``` error[E0382]: use of moved value: `receiver` -``` - -``` - --> src/lib.rs:2:42 -``` - -``` + --> src/lib.rs:26:42 | -``` - -``` -2 | let (sender, receiver) = mpsc::channel(); -``` - -``` +21 | let (sender, receiver) = mpsc::channel(); | -------- move occurs because `receiver` has type -``` - -``` `std::sync::mpsc::Receiver`, which does not implement the `Copy` trait -``` - -``` ... -``` - -``` -2 | workers.push(Worker::new(id, receiver)); -``` - -``` +26 | workers.push(Worker::new(id, receiver)); | ^^^^^^^^ value moved here, in -``` - -``` - -``` - -``` previous iteration of loop ``` @@ -2566,7 +1358,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`; @@ -2574,173 +1367,61 @@ 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 20-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 20-18 shows the changes we need to +make. Filename: src/lib.rs ``` use std::{ -``` - -``` sync::{mpsc, Arc, Mutex}, -``` - -``` thread, -``` - -``` }; -``` - -``` --snip-- -``` -``` - -``` - -``` impl ThreadPool { -``` - -``` --snip-- -``` - -``` pub fn new(size: usize) -> ThreadPool { -``` - -``` assert!(size > 0); -``` -``` - -``` - -``` let (sender, receiver) = mpsc::channel(); -``` -``` - -``` - -``` 1 let receiver = Arc::new(Mutex::new(receiver)); -``` -``` - -``` - -``` let mut workers = Vec::with_capacity(size); -``` -``` - -``` - -``` for id in 0..size { -``` - -``` workers.push( -``` - -``` -Worker::new(id, Arc::clone(& 2 receiver)) -``` - -``` -); -``` - -``` + Worker::new(id, Arc::clone(& 2 receiver)) + ); } -``` -``` - -``` - -``` ThreadPool { workers, sender } -``` - -``` } -``` -``` - -``` - -``` --snip-- -``` - -``` } -``` -``` - -``` - -``` --snip-- -``` -``` - -``` - -``` impl Worker { -``` - -``` fn new( -``` - -``` -id: usize, -``` - -``` -receiver: Arc>> -``` - -``` -) -> Worker { -``` - -``` + id: usize, + receiver: Arc>>, + ) -> Worker { --snip-- -``` - -``` } -``` - -``` } ``` -Sharing the receiver among the workers using `Arc` and `Mutex` +Listing 20-18: Sharing the receiver among the `Worker` instances using `Arc` +and `Mutex` In `ThreadPool::new`, we put the receiver in an `Arc` and a `Mutex` [1]. For -each new worker, we clone the `Arc` to bump the reference count so the workers -can share ownership of the receiver [2]. +each new `Worker`, we clone the `Arc` to bump the reference count so the +`Worker` instances can share ownership of the receiver [2]. With these changes, the code compiles! We’re getting there! @@ -2756,78 +1437,27 @@ Filename: src/lib.rs ``` --snip-- -``` -``` - -``` - -``` type Job = Box; -``` -``` - -``` - -``` impl ThreadPool { -``` - -``` --snip-- -``` -``` - -``` - -``` pub fn execute(&self, f: F) -``` - -``` where -``` - -``` F: FnOnce() + Send + 'static, -``` - -``` { -``` - -``` 1 let job = Box::new(f); -``` -``` - -``` - -``` 2 self.sender.send(job).unwrap(); -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` --snip-- ``` -Creating a `Job` type alias for a `Box` that holds each closure and then -sending the job down the channel +Listing 20-19: Creating a `Job` type alias for a `Box` that holds each closure +and then sending the job down the channel After creating a new `Job` instance using the closure we get in `execute` [1], we send that job down the sending end of the channel [2]. We’re calling @@ -2838,7 +1468,7 @@ 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 @@ -2848,93 +1478,31 @@ Filename: src/lib.rs ``` --snip-- -``` -``` - -``` - -``` impl Worker { -``` - -``` fn new( -``` - -``` -id: usize, -``` - -``` -receiver: Arc>> -``` - -``` -) -> Worker { -``` - -``` + id: usize, + receiver: Arc>>, + ) -> Worker { let thread = thread::spawn(move || loop { -``` + let job = receiver + 1 .lock() + 2 .unwrap() + 3 .recv() + 4 .unwrap(); -``` - let job = receiver -``` - -``` -.lock() -``` - -``` -.unwrap() -``` - -``` -.recv() -``` - -``` -.unwrap(); -``` - -``` - -``` - -``` println!("Worker {id} got a job; executing."); -``` -``` - -``` - -``` job(); -``` - -``` }); -``` -``` - -``` - -``` Worker { id, thread } -``` - -``` } -``` - -``` } ``` -Receiving and executing the jobs in the worker’s thread +Listing 20-20: Receiving and executing the jobs in the `Worker` instance’s +thread Here, we first call `lock` on the `receiver` to acquire the mutex [1], and then we call `unwrap` to panic on any errors [2]. Acquiring a lock might fail if the @@ -2953,146 +1521,44 @@ The call to `recv` blocks, so if there is no job yet, the current thread will wait until a job becomes available. The `Mutex` ensures that only one `Worker` thread at a time is trying to request a job. -Our thread pool is now in a working state! Give it a `cargo` `run` and make -some requests: +Our thread pool is now in a working state! Give it a `cargo run` and make some +requests: ``` $ cargo run -``` - -``` Compiling hello v0.1.0 (file:///projects/hello) -``` - -``` warning: field is never read: `workers` -``` - -``` --> src/lib.rs:7:5 -``` - -``` | -``` - -``` 7 | workers: Vec, -``` - -``` | ^^^^^^^^^^^^^^^^^^^^ -``` - -``` | -``` - -``` = note: `#[warn(dead_code)]` on by default -``` -``` - -``` - -``` warning: field is never read: `id` -``` - -``` --> src/lib.rs:48:5 -``` - -``` | -``` - -``` 48 | id: usize, -``` - -``` | ^^^^^^^^^ -``` -``` - -``` - -``` warning: field is never read: `thread` -``` - -``` --> src/lib.rs:49:5 -``` - -``` | -``` - -``` 49 | thread: thread::JoinHandle<()>, -``` - -``` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -``` -``` - -``` - -``` warning: `hello` (lib) generated 3 warnings -``` - -``` Finished dev [unoptimized + debuginfo] target(s) in 1.40s -``` - -``` Running `target/debug/hello` -``` - -``` Worker 0 got a job; executing. -``` - -``` Worker 2 got a job; executing. -``` - -``` Worker 1 got a job; executing. -``` - -``` Worker 3 got a job; executing. -``` - -``` Worker 0 got a job; executing. -``` - -``` Worker 2 got a job; executing. -``` - -``` Worker 1 got a job; executing. -``` - -``` Worker 3 got a job; executing. -``` - -``` Worker 0 got a job; executing. -``` - -``` Worker 2 got a job; executing. ``` @@ -3102,89 +1568,38 @@ overloaded if the server receives a lot of requests. If we make a request to */sleep*, the server will be able to serve other requests by having another thread run them. -> NoteIf you open */sleep* in multiple browser windows simultaneously, they +> Note: If you open */sleep* in multiple browser windows simultaneously, they 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. -After learning about the `while` `let` loop in Chapter 18, you might be -wondering why we didn’t write the worker thread code as shown in Listing 20-21. +After learning about the `while let` loop in Chapter 18, you might be wondering +why we didn’t write the `Worker` thread code as shown in Listing 20-21. Filename: src/lib.rs ``` --snip-- -``` -``` - -``` - -``` impl Worker { -``` - -``` fn new( -``` - -``` -id: usize, -``` - -``` -receiver: Arc>> -``` - -``` -) -> Worker { -``` - -``` + id: usize, + receiver: Arc>>, + ) -> Worker { let thread = thread::spawn(move || { -``` - -``` while let Ok(job) = receiver.lock().unwrap().recv() { -``` - -``` println!("Worker {id} got a job; executing."); -``` -``` - -``` - -``` job(); -``` - -``` } -``` - -``` }); -``` -``` - -``` - -``` Worker { id, thread } -``` - -``` } -``` - -``` } ``` -An alternative implementation of `Worker::new` using `while` `let` +Listing 20-21: An alternative implementation of `Worker::new` using `while let` This code compiles and runs but doesn’t result in the desired threading behavior: a slow request will still cause other requests to wait to be @@ -3197,13 +1612,13 @@ lock. However, this implementation can also result in the lock being held longer than intended if we aren’t mindful of the lifetime of the `MutexGuard`. -The code in Listing 20-20 that uses `let` `job` `=` -`receiver.lock().unwrap().recv().unwrap();` works because with `let`, any +The code in Listing 20-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 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 20-21, the lock remains held for the -duration of the call to `job()`, meaning other workers cannot receive jobs. +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 20-21, the lock remains held for the duration +of the call to `job()`, meaning other `Worker` instances cannot receive jobs. ## Graceful Shutdown and Cleanup @@ -3232,117 +1647,41 @@ Filename: src/lib.rs ``` impl Drop for ThreadPool { -``` - -``` fn drop(&mut self) { -``` - -``` 1 for worker in &mut self.workers { -``` - -``` 2 println!("Shutting down worker {}", worker.id); -``` -``` - -``` - -``` 3 worker.thread.join().unwrap(); -``` - -``` } -``` - -``` } -``` - -``` } ``` -Joining each thread when the thread pool goes out of scope +Listing 20-22: Joining each thread when the thread pool goes out of scope First we loop through each of the thread pool `workers` [1]. 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 [2], and then we call `join` on that -worker’s thread [3]. If the call to `join` fails, we use `unwrap` to make Rust -panic and go into an ungraceful shutdown. +mutate `worker`. For each `worker`, we print a message saying that this +particular `Worker` instance is shutting down [2], and then we call `join` on +that `Worker` instance’s thread [3]. 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: ``` error[E0507]: cannot move out of `worker.thread` which is behind a mutable -``` - -``` reference -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - -``` - + --> src/lib.rs:52:13 + | +52 | worker.thread.join().unwrap(); + | ^^^^^^^^^^^^^ ------ `worker.thread` moved due to this +method call + | | + | move occurs because `worker.thread` has type +`JoinHandle<()>`, which does not implement the `Copy` trait + | +note: this function takes ownership of the receiver `self`, which moves +`worker.thread` ``` The error tells us we can’t call `join` because we only have a mutable borrow @@ -3362,17 +1701,8 @@ Filename: src/lib.rs ``` struct Worker { -``` - -``` id: usize, -``` - -``` thread: Option>, -``` - -``` } ``` @@ -3381,90 +1711,26 @@ Checking this code, we get two errors: ``` error[E0599]: no method named `join` found for enum `Option` in the current -``` - -``` scope -``` - -``` --> src/lib.rs:52:27 -``` - -``` | -``` - -``` 52 | worker.thread.join().unwrap(); -``` - -``` | ^^^^ method not found in -``` - -``` `Option>` -``` -``` - -``` - -``` error[E0308]: mismatched types -``` - -``` --> src/lib.rs:72:22 -``` - -``` | -``` - -``` 72 | Worker { id, thread } -``` - -``` | ^^^^^^ expected enum `Option`, found struct -``` - -``` `JoinHandle` -``` - -``` | -``` - -``` = note: expected enum `Option>` -``` - -``` found struct `JoinHandle<_>` -``` - -``` help: try wrapping the expression in `Some` -``` - -``` | -``` - -``` - -``` - -``` - -``` - -``` - +72 | Worker { id, thread: Some(thread) } + | +++++++++++++ + ``` Let’s address the second error, which points to the code at the end of @@ -3475,53 +1741,17 @@ Filename: src/lib.rs ``` impl Worker { -``` - -``` fn new( -``` - -``` -id: usize, -``` - -``` -receiver: Arc>> -``` - -``` -) -> Worker { -``` - -``` + id: usize, + receiver: Arc>>, + ) -> Worker { --snip-- -``` -``` - -``` - -``` Worker { -``` - -``` id, -``` - -``` thread: Some(thread), -``` - -``` } -``` - -``` } -``` - -``` } ``` @@ -3533,53 +1763,23 @@ Filename: src/lib.rs ``` impl Drop for ThreadPool { -``` - -``` fn drop(&mut self) { -``` - -``` for worker in &mut self.workers { -``` - -``` println!("Shutting down worker {}", worker.id); -``` -``` - -``` - -``` 1 if let Some(thread) = worker.thread.take() { -``` - -``` 2 thread.join().unwrap(); -``` - -``` } -``` - -``` } -``` - -``` } -``` - -``` } ``` As discussed in Chapter 17, the `take` method on `Option` takes the `Some` -variant out and leaves `None` in its place. We’re using `if` `let` to -destructure the `Some` and get the thread [1]; then we call `join` on the -thread [2]. If a worker’s thread is already `None`, we know that worker has -already had its thread cleaned up, so nothing happens in that case. +variant out and leaves `None` in its place. We’re using `if let` to destructure +the `Some` and get the thread [1]; then we call `join` on the thread [2]. If a +`Worker` instance’s thread is already `None`, we know that `Worker` has already +had its thread cleaned up, so nothing happens in that case. ### Signaling to the Threads to Stop Listening for Jobs @@ -3589,7 +1789,7 @@ 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. +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. @@ -3604,307 +1804,94 @@ Filename: src/lib.rs ``` pub struct ThreadPool { -``` - -``` workers: Vec, -``` - -``` sender: Option>, -``` - -``` } -``` - -``` --snip-- -``` - -``` impl ThreadPool { -``` - -``` pub fn new(size: usize) -> ThreadPool { -``` - -``` --snip-- -``` -``` - -``` - -``` ThreadPool { -``` - -``` workers, -``` - -``` sender: Some(sender), -``` - -``` } -``` - -``` } -``` -``` - -``` - -``` pub fn execute(&self, f: F) -``` - -``` where -``` - -``` F: FnOnce() + Send + 'static, -``` - -``` { -``` - -``` let job = Box::new(f); -``` -``` - -``` - -``` self.sender -``` - -``` -.as_ref() -``` - -``` -.unwrap() -``` - -``` -.send(job) -``` - -``` -.unwrap(); -``` - -``` + .as_ref() + .unwrap() + .send(job) + .unwrap(); } -``` - -``` } -``` -``` - -``` - -``` impl Drop for ThreadPool { -``` - -``` fn drop(&mut self) { -``` - -``` 1 drop(self.sender.take()); -``` -``` - -``` - -``` for worker in &mut self.workers { -``` - -``` println!("Shutting down worker {}", worker.id); -``` -``` - -``` - -``` if let Some(thread) = worker.thread.take() { -``` - -``` thread.join().unwrap(); -``` - -``` } -``` - -``` } -``` - -``` } -``` - -``` } ``` -Explicitly dropping `sender` before joining the worker threads +Listing 20-23: Explicitly dropping `sender` before joining the `Worker` threads Dropping `sender` [1] 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 20-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. +be sent. When that happens, all the calls to `recv` that the `Worker` instances +do in the infinite loop will return an error. In Listing 20-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. Filename: src/lib.rs ``` impl Worker { -``` - -``` fn new( -``` - -``` -id: usize, -``` - -``` -receiver: Arc>> -``` - -``` -) -> Worker { -``` - -``` + id: usize, + receiver: Arc>>, + ) -> Worker { let thread = thread::spawn(move || loop { -``` + let message = receiver.lock().unwrap().recv(); -``` - -``` - -``` - -``` - -``` - -``` - -``` + match message { Ok(job) => { -``` - -``` println!( -``` + "Worker {id} got a job; executing." + ); -``` -"Worker {id} got a job; executing." -``` - -``` -); -``` - -``` - -``` - -``` job(); -``` - -``` } -``` - -``` Err(_) => { -``` - -``` println!( -``` - -``` -"Worker {id} shutting down." -``` - -``` -); -``` - -``` + "Worker {id} shutting down." + ); break; -``` - -``` } -``` - -``` } -``` - -``` }); -``` -``` - -``` - -``` Worker { -``` - -``` id, -``` - -``` thread: Some(thread), -``` - -``` } -``` - -``` } -``` - -``` } ``` -Explicitly breaking out of the loop when `recv` returns an error +Listing 20-24: Explicitly breaking out of the loop when `recv` returns an error 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 20-25. @@ -3913,61 +1900,23 @@ Filename: src/main.rs ``` fn main() { -``` - -``` let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); -``` - -``` let pool = ThreadPool::new(4); -``` -``` - -``` - -``` for stream in listener.incoming().take(2) { -``` - -``` let stream = stream.unwrap(); -``` -``` - -``` - -``` pool.execute(|| { -``` - -``` handle_connection(stream); -``` - -``` }); -``` - -``` } -``` -``` - -``` - -``` println!("Shutting down."); -``` - -``` } ``` -Shutting down the server after serving two requests by exiting the loop +Listing 20-25: Shutting down the server after serving two requests by exiting +the loop You wouldn’t want a real-world web server to shut down after serving only two requests. This code just demonstrates that the graceful shutdown and cleanup is @@ -3977,89 +1926,48 @@ The `take` method is defined in the `Iterator` trait and limits the iteration to the first two items at most. The `ThreadPool` will go out of scope at the end of `main`, and the `drop` implementation will run. -Start the server with `cargo` `run`, and make three requests. The third request +Start the server with `cargo run`, and make three requests. The third request should error, and in your terminal you should see output similar to this: ``` $ cargo run -``` - -``` Compiling hello v0.1.0 (file:///projects/hello) -``` - -``` Finished dev [unoptimized + debuginfo] target(s) in 1.0s -``` - -``` Running `target/debug/hello` -``` - -``` Worker 0 got a job; executing. -``` - -``` Shutting down. -``` - -``` Shutting down worker 0 -``` - -``` Worker 3 got a job; executing. -``` - -``` Worker 1 disconnected; shutting down. -``` - -``` Worker 2 disconnected; shutting down. -``` - -``` Worker 3 disconnected; shutting down. -``` - -``` Worker 0 disconnected; shutting down. -``` - -``` Shutting down worker 1 -``` - -``` 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 shutdown of the server, which cleans up all the threads in the pool. See -*https://www.nostarch.com/Rust2021* to download the full code for this chapter +*https://www.nostarch.com/Rust2021* to download the full code for this chapter for reference. We could do more here! If you want to continue enhancing this project, here are @@ -4069,9 +1977,10 @@ some ideas: * Add tests of the library’s functionality. * Change calls to `unwrap` to more robust error handling. * Use `ThreadPool` to perform some task other than serving web requests. -* Find a thread pool crate on *https://crates.io* and implement a similar web +* Find a thread pool crate on *https://crates.io* and implement a similar web server using the crate instead. Then compare its API and robustness to the thread pool we implemented. + ## Summary Well done! You’ve made it to the end of the book! We want to thank you for diff --git a/nostarch/frontmatter.md b/nostarch/frontmatter.md index 8411a0a8..001f9648 100644 --- a/nostarch/frontmatter.md +++ b/nostarch/frontmatter.md @@ -1,3 +1,8 @@ + ## About the Authors Carol Nichols is a member of the Rust Crates.io Team and a former member of the @@ -100,6 +105,7 @@ multiple files with modules. captures, the `move` keyword, and the `Fn` traits. * We fixed a number of small errors and imprecise wording throughout the book. Thank you to the readers who reported them! + Note that any code from earlier renditions of this book that compiled will continue to compile with the relevant edition in the project’s *Cargo.toml*, even as you update the Rust compiler version you’re using. That’s Rust’s @@ -136,10 +142,11 @@ Rust also brings contemporary developer tools to the systems programming world: * Cargo, the included dependency manager and build tool, makes adding, compiling, and managing dependencies painless and consistent across the Rust ecosystem. -* The `r``ustfmt` formatting tool ensures a consistent coding style across +* The `rustfmt` formatting tool ensures a consistent coding style across developers. * The Rust Language Server powers integrated development environment (IDE) integration for code completion and inline error messages. + By using these and other tools in the Rust ecosystem, developers can be productive while writing systems-level code.