diff --git a/appendix-03-derivable-traits.html b/appendix-03-derivable-traits.html
index a23fe4052..f590d4ea6 100644
--- a/appendix-03-derivable-traits.html
+++ b/appendix-03-derivable-traits.html
@@ -211,7 +211,7 @@ it can’t provide appropriate default behavior for you.
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 the
-“Macros” section of Chapter 19.
+“Macros” section of Chapter 20.
The Debug trait enables debug formatting in format strings, which you
indicate by adding :? within {} placeholders.
diff --git a/ch00-00-introduction.html b/ch00-00-introduction.html
index 31677624b..a48db31f6 100644
--- a/ch00-00-introduction.html
+++ b/ch00-00-introduction.html
@@ -295,15 +295,17 @@ functional programming languages. In Chapter 14, we’ll examine Cargo in more
depth and talk about best practices for sharing your libraries with others.
Chapter 15 discusses smart pointers that the standard library provides and the
traits that enable their functionality.
-
In Chapter 16, we’ll walk through different models of concurrent programming
-and talk about how Rust helps you to program in multiple threads fearlessly.
-Chapter 17 looks at how Rust idioms compare to object-oriented programming
+
In Chapter 16, we’ll walk through different models of concurrent programming and
+talk about how Rust helps you to program in multiple threads fearlessly. In
+Chapter 17, we will build on that by exploring Rust’s async and await syntax and
+the lightweight concurrency model they support.
+
Chapter 18 looks at how Rust idioms compare to object-oriented programming
principles you might be familiar with.
-
Chapter 18 is a reference on patterns and pattern matching, which are powerful
-ways of expressing ideas throughout Rust programs. Chapter 19 contains a
+
Chapter 19 is a reference on patterns and pattern matching, which are powerful
+ways of expressing ideas throughout Rust programs. Chapter 20 contains a
smorgasbord of advanced topics of interest, including unsafe Rust, macros, and
more about lifetimes, traits, types, functions, and closures.
-
In Chapter 20, we’ll complete a project in which we’ll implement a low-level
+
In Chapter 21, we’ll complete a project in which we’ll implement a low-level
multithreaded web server!
Finally, some appendices contain useful information about the language in a
more reference-like format. Appendix A covers Rust’s keywords, Appendix B
diff --git a/ch01-02-hello-world.html b/ch01-02-hello-world.html
index 06efe1d27..123150dd4 100644
--- a/ch01-02-hello-world.html
+++ b/ch01-02-hello-world.html
@@ -274,7 +274,7 @@ screen. There are four important details to notice here.
First, Rust style is to indent with four spaces, not a tab.
Second, println! calls a Rust macro. If it had called a function instead, it
would be entered as println (without the !). We’ll discuss Rust macros in
-more detail in Chapter 19. For now, you just need to know that using a !
+more detail in Chapter 20. For now, you just need to know that using a !
means that you’re calling a macro instead of a normal function and that macros
don’t always follow the same rules as functions.
Third, you see the "Hello, world!" string. We pass this string as an argument
diff --git a/ch02-00-guessing-game-tutorial.html b/ch02-00-guessing-game-tutorial.html
index 0e474d862..e760048d8 100644
--- a/ch02-00-guessing-game-tutorial.html
+++ b/ch02-00-guessing-game-tutorial.html
@@ -261,7 +261,7 @@ fn main() {
println!("You guessed: {}", guess);
}
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
io input/output library into scope. The io library comes from the standard
@@ -824,7 +824,7 @@ fits that arm’s pattern. Rust takes the value given to match and
through each arm’s pattern in turn. Patterns and the match construct are
powerful Rust features: they let you express a variety of situations your code
might encounter and they make sure you handle them all. These features will be
-covered in detail in Chapter 6 and Chapter 18, respectively.
+covered in detail in Chapter 6 and Chapter 19, respectively.
Let’s walk through an example with the match expression we use here. Say that
the user has guessed 50 and the randomly generated secret number this time is
38.
diff --git a/ch05-01-defining-structs.html b/ch05-01-defining-structs.html
index ce58ab3c2..f2f0c56d9 100644
--- a/ch05-01-defining-structs.html
+++ b/ch05-01-defining-structs.html
@@ -442,7 +442,9 @@ example, a function that takes a parameter of type Color cannot tak
Point as an argument, even though both types are made up of three i32
values. Otherwise, tuple struct instances are similar to tuples in that you can
destructure them into their individual pieces, and you can use a . followed
-by the index to access an individual value.
+by the index to access an individual value. Unlike tuples, tuple structs
+require you to name the type of the struct when you destructure them. For
+example, we would write let Point(x, y, z) = point.
You can also define structs that don’t have any fields! These are called
unit-like structs because they behave similarly to (), the unit type that
diff --git a/ch08-01-vectors.html b/ch08-01-vectors.html
index c4a717fa0..701c7caea 100644
--- a/ch08-01-vectors.html
+++ b/ch08-01-vectors.html
@@ -407,7 +407,7 @@ the vector. Using an enum plus a match expression means that Rust w
at compile time that every possible case is handled, as discussed in Chapter 6.
If you don’t know the exhaustive set of types a program will get at runtime to
store in a vector, the enum technique won’t work. Instead, you can use a trait
-object, which we’ll cover in Chapter 17.
+object, which we’ll cover in Chapter 18.
Now that we’ve discussed some of the most common ways to use vectors, be sure
to review the API documentation for all of the many
useful methods defined on Vec<T> by the standard library. For example, in
diff --git a/ch09-02-recoverable-errors-with-result.html b/ch09-02-recoverable-errors-with-result.html
index 15ae12edb..015997abb 100644
--- a/ch09-02-recoverable-errors-with-result.html
+++ b/ch09-02-recoverable-errors-with-result.html
@@ -699,7 +699,7 @@ fn main() -> Result<(), Box<dyn Error>> {
allows the use of the ? operator on Result values.
The Box<dyn Error> type is a trait object, which we’ll talk about in the
“Using Trait Objects that Allow for Values of Different
-Types” section in Chapter 17. For now, you can
+Types” section in Chapter 18. For now, you can
read Box<dyn Error> to mean “any kind of error.” Using ? on a Result
value in a main function with the error type Box<dyn Error> is allowed
because it allows any Err value to be returned early. Even though the body of
diff --git a/ch09-03-to-panic-or-not-to-panic.html b/ch09-03-to-panic-or-not-to-panic.html
index d56ec4144..f7b1b016a 100644
--- a/ch09-03-to-panic-or-not-to-panic.html
+++ b/ch09-03-to-panic-or-not-to-panic.html
@@ -252,7 +252,7 @@ format.
rather than checking for the problem at every step.
There’s not a good way to encode this information in the types you use. We’ll
work through an example of what we mean in the “Encoding States and Behavior
-as Types” section of Chapter 17.
+as Types” section of Chapter 18.
If someone calls your code and passes in values that don’t make sense, it’s
best to return an error if you can so the user of the library can decide what
diff --git a/ch10-02-traits.html b/ch10-02-traits.html
index 1c5b20414..93bd3c75e 100644
--- a/ch10-02-traits.html
+++ b/ch10-02-traits.html
@@ -660,7 +660,7 @@ around how the impl Trait syntax is implemented in the compiler. We
how to write a function with this behavior in the “Using Trait Objects That
Allow for Values of Different
Types” section of Chapter 17.
By using a trait bound with an impl block that uses generic type parameters,
we can implement methods conditionally for types that implement the specified
diff --git a/ch10-03-lifetime-syntax.html b/ch10-03-lifetime-syntax.html
index 6b8fa272c..958e718bd 100644
--- a/ch10-03-lifetime-syntax.html
+++ b/ch10-03-lifetime-syntax.html
@@ -886,7 +886,7 @@ behavior the code needs. You learned how to use lifetime annotations to ensure
that this flexible code won’t have any dangling references. And all of this
analysis happens at compile time, which doesn’t affect runtime performance!
Believe it or not, there is much more to learn on the topics we discussed in
-this chapter: Chapter 17 discusses trait objects, which are another way to use
+this chapter: Chapter 18 discusses trait objects, which are another way to use
traits. There are also more complex scenarios involving lifetime annotations
that you will only need in very advanced scenarios; for those, you should read
the Rust Reference. But next, you’ll learn how to write tests in
diff --git a/ch12-00-an-io-project.html b/ch12-00-an-io-project.html
index 9ad28f3eb..4d78ef451 100644
--- a/ch12-00-an-io-project.html
+++ b/ch12-00-an-io-project.html
@@ -213,7 +213,7 @@ background knowledge you need to understand a real-world project such as
diff --git a/ch12-03-improving-error-handling-and-modularity.html b/ch12-03-improving-error-handling-and-modularity.html
index 46fa34692..f19713cad 100644
--- a/ch12-03-improving-error-handling-and-modularity.html
+++ b/ch12-03-improving-error-handling-and-modularity.html
@@ -779,7 +779,7 @@ returned the unit type, (), and we keep that as the value returned
Ok case.
For the error type, we used the trait objectBox<dyn Error> (and we’ve
brought std::error::Error into scope with a use statement at the top).
-We’ll cover trait objects in Chapter 17. For now, just
+We’ll cover trait objects in Chapter 18. For now, just
know that Box<dyn Error> means the function will return a type that
implements the Error trait, but we don’t have to specify what particular type
the return value will be. This gives us flexibility to return error values that
diff --git a/ch13-02-iterators.html b/ch13-02-iterators.html
index c8c6bc2ab..f6420727a 100644
--- a/ch13-02-iterators.html
+++ b/ch13-02-iterators.html
@@ -245,7 +245,7 @@ standard library. The definition of the trait looks like this:
}
Notice this definition uses some new syntax: type Item and Self::Item,
which are defining an associated type with this trait. We’ll talk about
-associated types in depth in Chapter 19. For now, all you need to know is that
+associated types in depth in Chapter 20. For now, all you need to know is that
this code says implementing the Iterator trait requires that you also define
an Item type, and this Item type is used in the return type of the next
method. In other words, the Item type will be the type returned from the
diff --git a/ch14-02-publishing-to-crates-io.html b/ch14-02-publishing-to-crates-io.html
index f4affd706..6f0f334f9 100644
--- a/ch14-02-publishing-to-crates-io.html
+++ b/ch14-02-publishing-to-crates-io.html
@@ -246,7 +246,7 @@ 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
+Chapter 20), there should be a section explaining why the function is unsafe
and covering the invariants that the function expects callers to uphold.
Most documentation comments don’t need all of these sections, but this is a
diff --git a/ch15-01-box.html b/ch15-01-box.html
index c19d361bc..2b9467b5c 100644
--- a/ch15-01-box.html
+++ b/ch15-01-box.html
@@ -203,10 +203,10 @@ time because the data is copied around on the stack. To improve performance in
this situation, we can store the large amount of data on the heap in a box.
Then, only the small amount of pointer data is copied around on the stack,
while the data it references stays in one place on the heap. The third case is
-known as a trait object, and Chapter 17 devotes an entire section, “Using
+known as a trait object, and Chapter 18 devotes an entire section, “Using
Trait Objects That Allow for Values of Different Types,” just to that topic. So what you learn here you’ll apply again in
-Chapter 17!
Before we discuss the heap storage use case for Box<T>, we’ll cover the
syntax and how to interact with values stored within a Box<T>.
@@ -418,7 +418,7 @@ 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, too.
+in Chapter 18, too.
The Box<T> type is a smart pointer because it implements the Deref trait,
which allows Box<T> values to be treated like references. When a Box<T>
value goes out of scope, the heap data that the box is pointing to is cleaned
diff --git a/ch15-02-deref.html b/ch15-02-deref.html
index 99e6d127f..596867d68 100644
--- a/ch15-02-deref.html
+++ b/ch15-02-deref.html
@@ -360,7 +360,7 @@ impl<T> Deref for MyBox<T> {
The type Target = T; syntax defines an associated type for the Deref
trait to use. Associated types are a slightly different way of declaring a
generic parameter, but you don’t need to worry about them for now; we’ll cover
-them in more detail in Chapter 19.
+for us; we will discuss unsafe code more in Chapter 20.
We can use types that use the interior mutability pattern only when we can
ensure that the borrowing rules will be followed at runtime, even though the
compiler can’t guarantee that. The unsafe code involved is then wrapped in a
diff --git a/ch16-02-message-passing.html b/ch16-02-message-passing.html
index e7de7f65d..e2d93f414 100644
--- a/ch16-02-message-passing.html
+++ b/ch16-02-message-passing.html
@@ -230,7 +230,7 @@ receiver. The abbreviations tx and rx are traditionall
for transmitter and receiver respectively, so we name our variables as such
to indicate each end. We’re using a let statement with a pattern that
destructures the tuples; we’ll discuss the use of patterns in let statements
-and destructuring in Chapter 18. For now, know that using a let statement
+and destructuring in Chapter 19. For now, know that using a let statement
this way is a convenient approach to extract the pieces of the tuple returned
by mpsc::channel.
Let’s move the transmitting end into a spawned thread and have it send one
diff --git a/ch16-04-extensible-concurrency-sync-and-send.html b/ch16-04-extensible-concurrency-sync-and-send.html
index a592496b6..799cfe1ab 100644
--- a/ch16-04-extensible-concurrency-sync-and-send.html
+++ b/ch16-04-extensible-concurrency-sync-and-send.html
@@ -203,7 +203,7 @@ this in Listing 16-14, we got the error the trait Send is not implemented
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
-we’ll discuss in Chapter 19.
The Sync marker trait indicates that it is safe for the type implementing
Sync to be referenced from multiple threads. In other words, any type T is
@@ -223,14 +223,14 @@ also Send and Sync, we don’t have to implement those
marker traits, they don’t even have any methods to implement. They’re just
useful for enforcing invariants related to concurrency.
Manually implementing these traits involves implementing unsafe Rust code.
-We’ll talk about using unsafe Rust code in Chapter 19; for now, the important
+We’ll talk about using unsafe Rust code in Chapter 20; for now, the important
information is that building new concurrent types not made up of Send and
Sync parts requires careful thought to uphold the safety guarantees. “The
Rustonomicon” has more information about these guarantees and how to
uphold them.
This isn’t the last you’ll see of concurrency in this book: the whole next
-chapter focuses on async programming, and the project in Chapter 20 will use the
+chapter focuses on async programming, and the project in Chapter 21 will use the
concepts in this chapter in a more realistic situation than the smaller examples
discussed here.
As mentioned earlier, because very little of how Rust handles concurrency is
diff --git a/ch18-01-what-is-oo.html b/ch18-01-what-is-oo.html
index e8e93a186..a8e71d5fa 100644
--- a/ch18-01-what-is-oo.html
+++ b/ch18-01-what-is-oo.html
@@ -217,21 +217,23 @@ can define a struct AveragedCollection that has a field containing
of i32 values. The struct can also have a field that contains the average of
the values in the vector, meaning the average doesn’t have to be computed
on demand whenever anyone needs it. In other words, AveragedCollection will
-cache the calculated average for us. Listing 17-1 has the definition of the
+cache the calculated average for us. Listing 18-1 has the definition of the
AveragedCollection struct:
Listing 17-1: An AveragedCollection struct that
-maintains a list of integers and the average of the items in the
-collection
+Listing 18-1: Listing 18-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
ensure that whenever a value is added or removed from the list, the average is
also updated. We do this by implementing add, remove, and average methods
-on the struct, as shown in Listing 17-2:
+on the struct, as shown in Listing 18-2:
+
+Filename: src/lib.rs
Filename: src/lib.rs
pub struct AveragedCollection {
list: Vec<i32>,
@@ -264,8 +266,8 @@ on the struct, as shown in Listing 17-2:
self.average = total as f64 / self.list.len() as f64;
}
}
-
Listing 17-2: Implementations of the public methods
-add, remove, and average on AveragedCollection
+Listing 18-2: Listing 18-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 list using the add method or removed using the remove method, the
diff --git a/ch18-02-trait-objects.html b/ch18-02-trait-objects.html
index f98db3888..e73a2a53a 100644
--- a/ch18-02-trait-objects.html
+++ b/ch18-02-trait-objects.html
@@ -221,7 +221,7 @@ implementing our specified trait and a table used to look up trait methods on
that type at runtime. We create a trait object by specifying some sort of
pointer, such as a & reference or a Box<T> smart pointer, then the dyn
keyword, and then specifying the relevant trait. (We’ll talk about the reason
-trait objects must use a pointer in Chapter 19 in the section “Dynamically
+trait objects must use a pointer in Chapter 20 in the section “Dynamically
Sized Types and the Sized Trait.”) We can
use trait objects in place of a generic or concrete type. Wherever we use a
trait object, Rust’s type system will ensure at compile time that any value
@@ -237,19 +237,22 @@ But trait objects differ from traditional objects in that we can’t add data to
a trait object. Trait objects aren’t as generally useful as objects in other
languages: their specific purpose is to allow abstraction across common
behavior.
-
Listing 17-3 shows how to define a trait named Draw with one method named
+
Listing 18-3 shows how to define a trait named Draw with one method named
draw:
-
Filename: src/lib.rs
+
+Filename: src/lib.rs
pub trait Draw {
fn draw(&self);
}
-
Listing 17-3: Definition of the Draw trait
+Listing 18-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
+in Chapter 10. Next comes some new syntax: Listing 18-4 defines a struct named
Screen that holds a vector named components. This vector is of type
Box<dyn Draw>, which is a trait object; it’s a stand-in for any type inside
a Box that implements the Draw trait.
-
Filename: src/lib.rs
+
+Filename: src/lib.rs
pub trait Draw {
fn draw(&self);
}
@@ -257,12 +260,12 @@ a Box that implements the Draw trait.
pub struct Screen {
pub components: Vec<Box<dyn Draw>>,
}
-
Listing 17-4: Definition of the Screen struct with a
-components field holding a vector of trait objects that implement the Draw
-trait
+Listing 18-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:
-
Filename: src/lib.rs
+draw method on each of its components, as shown in Listing 18-5:
+
+Filename: src/lib.rs
Listing 17-5: A run method on Screen that calls the
-draw method on each component
+Listing 18-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
with one concrete type at a time, whereas trait objects allow for multiple
concrete types to fill in for the trait object at runtime. For example, we
could have defined the Screen struct using a generic type and a trait bound
-as in Listing 17-6:
Listing 17-6: An alternate implementation of the Screen
-struct and its run method using generics and trait bounds
+Listing 18-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
collections, using generics and trait bounds is preferable because the
@@ -320,8 +324,9 @@ runtime performance implications.
Button type. Again, actually implementing a GUI library is beyond the scope
of this book, so the draw method won’t have any useful implementation in its
body. To imagine what the implementation might look like, a Button struct
-might have fields for width, height, and label, as shown in Listing 17-7:
-
Filename: src/lib.rs
+might have fields for width, height, and label, as shown in Listing 18-7:
+
+Filename: src/lib.rs
pub trait Draw {
fn draw(&self);
}
@@ -349,8 +354,8 @@ impl Draw for Button {
// code to actually draw a button
}
}
-
Listing 17-7: A Button struct that implements the
-Draw trait
+Listing 18-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
same fields plus a placeholder field. Each of the types we want to draw on
@@ -362,8 +367,9 @@ happens when a user clicks the button. These kinds of methods won’t apply to
types like TextField.
If someone using our library decides to implement a SelectBox struct that has
width, height, and options fields, they implement the Draw trait on the
-SelectBox type as well, as shown in Listing 17-8:
-
Filename: src/main.rs
+SelectBox type as well, as shown in Listing 18-8:
+
+Filename: src/main.rs
use gui::Draw;
struct SelectBox {
@@ -379,14 +385,15 @@ impl Draw for SelectBox {
}
fn main() {}
-
Listing 17-8: Another crate using gui and implementing
-the Draw trait on a SelectBox struct
+Listing 18-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
by putting each in a Box<T> to become a trait object. They can then call the
run method on the Screen instance, which will call draw on each of the
-components. Listing 17-9 shows this implementation:
-
Filename: src/main.rs
+components. Listing 18-9 shows this implementation:
+
+Filename: src/main.rs
Listing 17-9: Using trait objects to store values of
-different types that implement the same trait
+Listing 18-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
new type and draw it because SelectBox implements the Draw trait, which
@@ -435,7 +442,7 @@ means it implements the draw method.
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
+in Listing 18-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<dyn Draw> as the type of the values in the components
@@ -446,9 +453,10 @@ similar to code using duck typing is that we never have to check whether a
value implements a particular method at runtime or worry about getting errors
if a value doesn’t implement a method but we call it anyway. Rust won’t compile
our code if the values don’t implement the traits that the trait objects need.
-
For example, Listing 17-10 shows what happens if we try to create a Screen
+
For example, Listing 18-10 shows what happens if we try to create a Screen
with a String as a component:
Listing 17-10: Attempting to use a type that doesn’t
-implement the trait object’s trait
+Listing 18-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:
$ cargo run
Compiling gui v0.1.0 (file:///projects/gui)
@@ -497,8 +505,8 @@ runtime, Rust uses the pointers inside the trait object to know which method to
call. This lookup incurs a runtime cost that doesn’t occur with static
dispatch. Dynamic dispatch also prevents the compiler from choosing to inline a
method’s code, which in turn prevents some optimizations. However, we did get
-extra flexibility in the code that we wrote in Listing 17-5 and were able to
-support in Listing 17-9, so it’s a trade-off to consider.
+extra flexibility in the code that we wrote in Listing 18-5 and were able to
+support in Listing 18-9, so it’s a trade-off to consider.
diff --git a/ch18-03-oo-design-patterns.html b/ch18-03-oo-design-patterns.html
index a9964241e..d71e70f2e 100644
--- a/ch18-03-oo-design-patterns.html
+++ b/ch18-03-oo-design-patterns.html
@@ -212,10 +212,11 @@ accidentally be published.
Any other changes attempted on a post should have no effect. For example, if we
try to approve a draft blog post before we’ve requested a review, the post
should remain an unpublished draft.
-
Listing 17-11 shows this workflow in code form: this is an example usage of the
+
Listing 18-11 shows this workflow in code form: this is an example usage of the
API we’ll implement in a library crate named blog. This won’t compile yet
because we haven’t implemented the blog crate.
-
Filename: src/main.rs
+
+Filename: src/main.rs
use blog::Post;
fn main() {
@@ -230,8 +231,8 @@ fn main() {
post.approve();
assert_eq!("I ate a salad for lunch today", post.content());
}
-
Listing 17-11: Code that demonstrates the desired
-behavior we want our blog crate to have
+Listing 18-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. We
want to allow text to be added to the blog post. If we try to get the post’s
content immediately, before approval, we shouldn’t get any text because the
@@ -255,13 +256,14 @@ make a mistake with the states, like publishing a post before it’s reviewed.
Let’s get started on the implementation of the library! We know we need a
public Post struct that holds some content, so we’ll start with the
definition of the struct and an associated public new function to create an
-instance of Post, as shown in Listing 17-12. We’ll also make a private
+instance of Post, as shown in Listing 18-12. We’ll also make a private
State trait that will define the behavior that all state objects for a Post
must have.
Then Post will hold a trait object of Box<dyn State> inside an Option<T>
in a private field named state to hold the state object. You’ll see why the
Option<T> is necessary in a bit.
-
Filename: src/lib.rs
+
+Filename: src/lib.rs
pub struct Post {
state: Option<Box<dyn State>>,
content: String,
@@ -281,9 +283,8 @@ trait State {}
struct Draft {}
impl State for Draft {}
-
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
+Listing 18-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
implement the State trait. For now, the trait doesn’t have any methods, and
@@ -296,13 +297,13 @@ a draft. Because the state field of Post is private, t
create a Post in any other state! In the Post::new function, we set the
content field to a new, empty String.
We saw in Listing 17-11 that we want to be able to call a method named
+
We saw in Listing 18-11 that we want to be able to call a method named
add_text and pass it a &str that is then added as the text content of the
blog post. We implement this as a method, rather than exposing the content
field as pub, so that later we can implement a method that will control how
the content field’s data is read. The add_text method is pretty
-straightforward, so let’s add the implementation in Listing 17-13 to the impl Post block:
-
Filename: src/lib.rs
+straightforward, so let’s add the implementation in Listing 18-13 to the impl Post block:
+
pub struct Post {
state: Option<Box<dyn State>>,
content: String,
@@ -327,8 +328,8 @@ straightforward, so let’s add the implementation in Listing 17-13 to the struct Draft {}
impl State for Draft {}
-
Listing 17-13: Implementing the add_text method to add
-text to a post’s content
+Listing 18-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
push_str on the String in content and pass the text argument to add to
@@ -339,13 +340,14 @@ support.
Even after we’ve called add_text and added some content to our post, we still
want the content method to return an empty string slice because the post is
-still in the draft state, as shown on line 7 of Listing 17-11. For now, let’s
+still in the draft state, as shown on line 7 of Listing 18-11. For now, let’s
implement the content method with the simplest thing that will fulfill this
requirement: always returning an empty string slice. We’ll change this later
once we implement the ability to change a post’s state so it can be published.
So far, posts can only be in the draft state, so the post content should always
-be empty. Listing 17-14 shows this placeholder implementation:
pub struct Post {
state: Option<Box<dyn State>>,
content: String,
@@ -374,14 +376,15 @@ be empty. Listing 17-14 shows this placeholder implementation:
struct Draft {}
impl State for Draft {}
-
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 line 7
+Listing 18-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 18-11 up to line 7
works as intended.
Next, we need to add functionality to request a review of a post, which should
-change its state from Draft to PendingReview. Listing 17-15 shows this code:
-
Filename: src/lib.rs
+change its state from Draft to PendingReview. Listing 18-15 shows this code:
+
+Filename: src/lib.rs
pub struct Post {
state: Option<Box<dyn State>>,
content: String,
@@ -430,8 +433,8 @@ impl State for PendingReview {
self
}
}
-
Listing 17-15: Implementing request_review methods on
-Post and the State trait
+Listing 18-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. Then we call an internal request_review method on the
current state of Post, and this second request_review method consumes the
@@ -466,14 +469,15 @@ state is responsible for its own rules.
We’ll leave the content method on Post as is, returning an empty string
slice. We can now have a Post in the PendingReview state as well as in the
Draft state, but we want the same behavior in the PendingReview state.
-Listing 17-11 now works up to line 10!
The approve method will be similar to the request_review method: it will
set state to the value that the current state says it should have when that
-state is approved, as shown in Listing 17-16:
-
Filename: src/lib.rs
+state is approved, as shown in Listing 18-16:
+
+Filename: src/lib.rs
pub struct Post {
state: Option<Box<dyn State>>,
content: String,
@@ -551,8 +555,8 @@ impl State for Published {
self
}
}
-
Listing 17-16: Implementing the approve method on
-Post and the State trait
+Listing 18-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.
Similar to the way request_review on PendingReview works, if we call the
@@ -565,8 +569,9 @@ state in those cases.
Now we need to update the content method on Post. We want the value
returned from content to depend on the current state of the Post, so we’re
going to have the Post delegate to a content method defined on its state,
-as shown in Listing 17-17:
-
Filename: src/lib.rs
+as shown in Listing 18-17:
+
+Filename: src/lib.rs
pub struct Post {
state: Option<Box<dyn State>>,
content: String,
@@ -643,8 +648,8 @@ as shown in Listing 17-17:
self
}
}
-
Listing 17-17: Updating the content method on Post to
-delegate to a content method on State
+Listing 18-17: Updating the content method on Post to delegate to a content method on State
+
Because the goal is to keep all these rules inside the structs that implement
State, we call a content method on the value in state and pass the post
instance (that is, self) as an argument. Then we return the value that’s
@@ -665,8 +670,9 @@ will take effect on the & and the Box so the State trait. That means
we need to add content to the State trait definition, and that is where
we’ll put the logic for what content to return depending on which state we
-have, as shown in Listing 17-18:
-
Filename: src/lib.rs
+have, as shown in Listing 18-18:
+
+Filename: src/lib.rs
pub struct Post {
state: Option<Box<dyn State>>,
content: String,
@@ -753,8 +759,8 @@ impl State for Published {
&post.content
}
}
-
Listing 17-18: Adding the content method to the State
-trait
+Listing 18-18: Adding the content method to the State trait
+
We add a default implementation for the content method that returns an empty
string slice. That means we don’t need to implement content on the Draft
and PendingReview structs. The Published struct will override the content
@@ -763,7 +769,7 @@ method and return the value in post.content.
Chapter 10. We’re taking a reference to a post as an argument and returning a
reference to part of that post, so the lifetime of the returned reference is
related to the lifetime of the post argument.
-
And we’re done—all of Listing 17-11 now works! We’ve implemented the state
+
And we’re done—all of Listing 18-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.
@@ -821,7 +827,7 @@ and approve methods on Post. Both methods delegate to
the same method on the value in the state field of Option and set the new
value of the state field to the result. If we had a lot of methods on Post
that followed this pattern, we might consider defining a macro to eliminate the
-repetition (see the “Macros” section in Chapter 19).
+repetition (see the “Macros” section in Chapter 20).
By implementing the state pattern exactly as it’s defined for object-oriented
languages, we’re not taking as full advantage of Rust’s strengths as we could.
Let’s look at some changes we can make to the blog crate that can make
@@ -832,8 +838,9 @@ trade-offs. Rather than encapsulating the states and transitions completely so
outside code has no knowledge of them, we’ll encode the states into different
types. Consequently, Rust’s type checking system will prevent attempts to use
draft posts where only published posts are allowed by issuing a compiler error.
-
Let’s consider the first part of main in Listing 17-11:
-
Filename: src/main.rs
+
Let’s consider the first part of main in Listing 18-11:
+
+Filename: src/main.rs
use blog::Post;
fn main() {
@@ -848,6 +855,7 @@ draft posts where only published posts are allowed by issuing a compiler error.<
post.approve();
assert_eq!("I ate a salad for lunch today", post.content());
}
+
We still enable the creation of new posts in the draft state using Post::new
and the ability to add text to the post’s content. But instead of having a
content method on a draft post that returns an empty string, we’ll make it so
@@ -855,9 +863,10 @@ draft posts don’t have the content method at all. That way, if we
a draft post’s content, we’ll get a compiler error telling us the method
doesn’t exist. As a result, it will be impossible for us to accidentally
display draft post content in production, because that code won’t even compile.
-Listing 17-19 shows the definition of a Post struct and a DraftPost struct,
+Listing 18-19 shows the definition of a Post struct and a DraftPost struct,
as well as methods on each:
Listing 17-19: A Post with a content method and a
-DraftPost without a content method
+Listing 18-19: A Post with a content method and 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
we’re moving the encoding of the state to the types of the structs. The Post
@@ -906,8 +915,9 @@ pending review state should still not display any content. Let’s implement
these constraints by adding another struct, PendingReviewPost, defining the
request_review method on DraftPost to return a PendingReviewPost, and
defining an approve method on PendingReviewPost to return a Post, as
-shown in Listing 17-20:
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
+Listing 18-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
them into a PendingReviewPost and a published Post, respectively. This way,
@@ -972,8 +981,9 @@ called on, so we need to add more let post = shadowing assignments
the returned instances. We also can’t have the assertions about the draft and
pending review posts’ contents be empty strings, nor do we need them: we can’t
compile code that tries to use the content of posts in those states any longer.
-The updated code in main is shown in Listing 17-21:
-
Filename: src/main.rs
+The updated code in main is shown in Listing 18-21:
+
+Filename: src/main.rs
use blog::Post;
fn main() {
@@ -987,8 +997,8 @@ fn main() {
assert_eq!("I ate a salad for lunch today", post.content());
}
-
Listing 17-21: Modifications to main to use the new
-implementation of the blog post workflow
+Listing 18-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:
the transformations between the states are no longer encapsulated entirely
@@ -997,7 +1007,7 @@ now impossible because of the type system and the type checking that happens at
compile time! This ensures that certain bugs, such as display of the content of
an unpublished post, will be discovered before they make it to production.
Try the tasks suggested at the start of this section on the blog crate as it
-is after Listing 17-21 to see what you think about the design of this version
+is after Listing 18-21 to see what you think about the design of this version
of the code. Note that some of the tasks might be completed already in this
design.
We’ve seen that even though Rust is capable of implementing object-oriented
diff --git a/ch19-01-all-the-places-for-patterns.html b/ch19-01-all-the-places-for-patterns.html
index 22b8f94c5..ec23ff53e 100644
--- a/ch19-01-all-the-places-for-patterns.html
+++ b/ch19-01-all-the-places-for-patterns.html
@@ -219,14 +219,15 @@ chapter.
way to write the equivalent of a match that only matches one case.
Optionally, if let can have a corresponding else containing code to run if
the pattern in the if let doesn’t match.
-
Listing 18-1 shows that it’s also possible to mix and match if let, else if, and else if let expressions. Doing so gives us more flexibility than a
+
Listing 19-1 shows that it’s also possible to mix and match if let, else if, and else if let expressions. Doing so gives us more flexibility than a
match expression in which we can express only one value to compare with the
patterns. Also, Rust doesn’t require that the conditions in a series of if let, else if, else if let arms relate to each other.
-
The code in Listing 18-1 determines what color to make your background based on
+
The code in Listing 19-1 determines what color to make your background based on
a series of checks for several conditions. For this example, we’ve created
variables with hardcoded values that a real program might receive from user
input.
-
Filename: src/main.rs
+
+Filename: src/main.rs
fn main() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
@@ -246,8 +247,8 @@ input.
println!("Using blue as the background color");
}
}
-
Listing 18-1: Mixing if let, else if, else if let,
-and else
+Listing 19-1: Mixing if let, else if, else if let, and else
+
If the user specifies a favorite color, that color is used as the background.
If no favorite color is specified and today is Tuesday, the background color is
green. Otherwise, if the user specifies their age as a string and we can parse
@@ -270,8 +271,9 @@ not alert us to the possible logic bug.
Similar in construction to if let, the while let conditional loop allows a
while loop to run for as long as a pattern continues to match. In Listing
-18-2 we code a while let loop that uses a vector as a stack and prints the
+19-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.
+
fn main() {
let mut stack = Vec::new();
@@ -283,8 +285,8 @@ values in the vector in the opposite order in which they were pushed.
println!("{top}");
}
}
-
Listing 18-2: Using a while let loop to print values
-for as long as stack.pop() returns Some
+Listing 19-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, pop
returns None. The while loop continues running the code in its block as
@@ -292,9 +294,10 @@ long as pop returns Some. When pop return
use while let to pop every element off our stack.
In a for loop, the value that directly follows the keyword for is a
-pattern. For example, in for x in y the x is the pattern. Listing 18-3
+pattern. For example, in for x in y the x is the pattern. Listing 19-3
demonstrates how to use a pattern in a for loop to destructure, or break
apart, a tuple as part of the for loop.
+
fn main() {
let v = vec!['a', 'b', 'c'];
@@ -302,9 +305,9 @@ apart, a tuple as part of the for loop.
println!("{value} is at index {index}");
}
}
-
Listing 18-3: Using a pattern in a for loop to
-destructure a tuple
-
The code in Listing 18-3 will print the following:
+Listing 19-3: Using a pattern in a for loop to destructure a tuple
+
+
The code in Listing 19-3 will print the following:
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.52s
@@ -339,25 +342,27 @@ the expression against the pattern and assigns any names it finds. So in the
the variable x.” Because the name x is the whole pattern, this pattern
effectively means “bind everything to the variable x, whatever the value is.”
To see the pattern matching aspect of let more clearly, consider Listing
-18-4, which uses a pattern with let to destructure a tuple.
+19-4, which uses a pattern with let to destructure a tuple.
+
fn main() {
let (x, y, z) = (1, 2, 3);
}
-
Listing 18-4: Using a pattern to destructure a tuple and
-create three variables at once
+Listing 19-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, so Rust
binds 1 to x, 2 to y, and 3 to z. You can think of this tuple
pattern as nesting three individual variable patterns inside it.
If the number of elements in the pattern doesn’t match the number of elements
in the tuple, the overall type won’t match and we’ll get a compiler error. For
-example, Listing 18-5 shows an attempt to destructure a tuple with three
+example, Listing 19-5 shows an attempt to destructure a tuple with three
elements into two variables, which won’t work.
+
fn main() {
let (x, y) = (1, 2, 3);
}
-
Listing 18-5: Incorrectly constructing a pattern whose
-variables don’t match the number of elements in the tuple
+Listing 19-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:
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
@@ -382,20 +387,22 @@ is that we have too many variables in the pattern, the solution is to make the
types match by removing variables so the number of variables equals the number
of elements in the tuple.
Function parameters can also be patterns. The code in Listing 18-6, which
+
Function parameters can also be patterns. The code in Listing 19-6, which
declares a function named foo that takes one parameter named x of type
i32, should by now look familiar.
+
fn foo(x: i32) {
// code goes here
}
fn main() {}
-
Listing 18-6: A function signature uses patterns in the
-parameters
+Listing 19-6: A function signature uses 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
+function’s arguments to the pattern. Listing 19-7 splits the values in a tuple
as we pass it to a function.
Listing 18-7: A function with parameters that destructure
-a tuple
+Listing 19-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.
We can also use patterns in closure parameter lists in the same way as in
diff --git a/ch19-02-refutability.html b/ch19-02-refutability.html
index b3dfe7202..d80245516 100644
--- a/ch19-02-refutability.html
+++ b/ch19-02-refutability.html
@@ -200,15 +200,16 @@ of refutability so you can respond when you see it in an error message. In
those cases, you’ll need to change either the pattern or the construct you’re
using the pattern with, depending on the intended behavior of the code.
Let’s look at an example of what happens when we try to use a refutable pattern
-where Rust requires an irrefutable pattern and vice versa. Listing 18-8 shows a
+where Rust requires an irrefutable pattern and vice versa. Listing 19-8 shows a
let statement, but for the pattern we’ve specified Some(x), a refutable
pattern. As you might expect, this code will not compile.
+
fn main() {
let some_option_value: Option<i32> = None;
let Some(x) = some_option_value;
}
-
Listing 18-8: Attempting to use a refutable pattern with
-let
+Listing 19-8: Attempting to use a refutable pattern with let
+
If some_option_value was a None value, it would fail to match the pattern
Some(x), meaning the pattern is refutable. However, the let statement can
only accept an irrefutable pattern because there is nothing valid the code can
@@ -223,7 +224,7 @@ error[E0005]: refutable pattern in local binding
| ^^^^^^^ 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: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `Option<i32>`
help: you might want to use `let else` to handle the variant that isn't matched
|
@@ -239,26 +240,28 @@ pattern Some(x), Rust rightfully produces a compiler error.
fix it by changing the code that uses the pattern: instead of using let, we
can use if let. Then if the pattern doesn’t match, the code will just skip
the code in the curly brackets, giving it a way to continue validly. Listing
-18-9 shows how to fix the code in Listing 18-8.
+19-9 shows how to fix the code in Listing 19-8.
+
fn main() {
let some_option_value: Option<i32> = None;
if let Some(x) = some_option_value {
println!("{x}");
}
}
-
Listing 18-9: Using if let and a block with refutable
-patterns instead of let
+Listing 19-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 now. However,
if we give if let an irrefutable pattern (a pattern that will always
-match), such as x, as shown in Listing 18-10, the compiler will give a
+match), such as x, as shown in Listing 19-10, the compiler will give a
warning.
+
fn main() {
if let x = 5 {
println!("{x}");
};
}
-
Listing 18-10: Attempting to use an irrefutable pattern
-with if let
+Listing 19-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:
$ cargo run
diff --git a/ch19-03-pattern-syntax.html b/ch19-03-pattern-syntax.html
index 266f51f6b..463231efa 100644
--- a/ch19-03-pattern-syntax.html
+++ b/ch19-03-pattern-syntax.html
@@ -205,12 +205,13 @@ them many times in the book. However, there is a complication when you use
named variables in match expressions. Because match starts a new scope,
variables declared as part of a pattern inside the match expression will
shadow those with the same name outside the match construct, as is the case
-with all variables. In Listing 18-11, we declare a variable named x with the
+with all variables. In Listing 19-11, we declare a variable named x with the
value Some(5) and a variable y with the value 10. We then create a
match expression on the value x. Look at the patterns in the match arms and
println! at the end, and try to figure out what the code will print before
running this code or reading further.
-
Filename: src/main.rs
+
+Filename: src/main.rs
fn main() {
let x = Some(5);
let y = 10;
@@ -223,8 +224,8 @@ running this code or reading further.
println!("at the end: x = {x:?}, y = {y}");
}
-
Listing 18-11: A match expression with an arm that
-introduces a shadowed variable y
+Listing 19-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 doesn’t match the defined value of x, so the code
continues.
@@ -298,9 +299,10 @@ numeric values, ranges are only allowed with numeric or char values
We can also use patterns to destructure structs, enums, and tuples to use
different parts of these values. Let’s walk through each value.
Listing 18-12: Destructuring a struct’s fields into
-separate variables
+Listing 19-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
variables in the pattern don’t have to match the field names of the struct.
@@ -323,10 +325,11 @@ easier to remember which variables came from which fields. Because of this
common usage, and because writing let Point { x: x, y: y } = p; contains a
lot of duplication, Rust has a shorthand for patterns that match struct fields:
you only need to list the name of the struct field, and the variables created
-from the pattern will have the same names. Listing 18-13 behaves in the same
-way as the code in Listing 18-12, but the variables created in the let
+from the pattern will have the same names. Listing 19-13 behaves in the same
+way as the code in Listing 19-12, but the variables created in the let
pattern are x and y instead of a and b.
Listing 18-13: Destructuring struct fields using struct
-field shorthand
+Listing 19-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
values from the p struct.
@@ -348,10 +351,11 @@ values from the p struct.
rather than creating variables for all the fields. Doing so allows us to test
some of the fields for particular values while creating variables to
destructure the other fields.
-
In Listing 18-14, we have a match expression that separates Point values
+
In Listing 19-14, we have a match expression that separates Point values
into three cases: points that lie directly on the x axis (which is true when
y = 0), on the y axis (x = 0), or neither.
-
Filename: src/main.rs
+
+Filename: src/main.rs
struct Point {
x: i32,
y: i32,
@@ -368,8 +372,8 @@ into three cases: points that lie directly on the x axis (which is
}
}
}
-
Listing 18-14: Destructuring and matching literal values
-in one pattern
+Listing 19-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
creates an x variable that we can use in the code for this arm.
@@ -386,9 +390,10 @@ and the y axis, this code would only print On the x axis at 0
We’ve destructured enums in this book (for example, Listing 6-5 in Chapter 6),
but haven’t yet explicitly discussed that the pattern to destructure an enum
corresponds to the way the data stored within the enum is defined. As an
-example, in Listing 18-15 we use the Message enum from Listing 6-2 and write
+example, in Listing 19-15 we use the Message enum from Listing 6-2 and write
a match with patterns that will destructure each inner value.
Listing 18-15: Destructuring enum variants that hold
-different kinds of values
+Listing 19-15: Destructuring enum variants that hold different kinds of values
+
This code will print Change the color to red 0, green 160, and blue 255. Try
changing the value of msg to see the code from the other arms run.
For enum variants without any data, like Message::Quit, we can’t destructure
@@ -425,7 +430,7 @@ and no variables are in that pattern.
similar to the pattern we specify to match structs. After the variant name, we
place curly brackets and then list the fields with variables so we break apart
the pieces to use in the code for this arm. Here we use the shorthand form as
-we did in Listing 18-13.
+we did in Listing 19-13.
For tuple-like enum variants, like Message::Write that holds a tuple with one
element and Message::ChangeColor that holds a tuple with three elements, the
pattern is similar to the pattern we specify to match tuples. The number of
@@ -434,8 +439,9 @@ matching.
So far, our examples have all been matching structs or enums one level deep,
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.
+code in Listing 19-15 to support RGB and HSV colors in the ChangeColor
+message, as shown in Listing 19-16.
+
The pattern of the first arm in the match expression matches a
Message::ChangeColor enum variant that contains a Color::Rgb variant; then
the pattern binds to the three inner i32 values. The pattern of the second
@@ -496,8 +503,9 @@ parts of a value. Let’s explore how and why to use each of these patterns.
We’ve used the underscore as a wildcard pattern that will match any value but
not bind to the value. This is especially useful as the last arm in a match
expression, but we can also use it in any pattern, including function
-parameters, as shown in Listing 18-17.
-
Filename: src/main.rs
+parameters, as shown in Listing 19-17.
+
+Filename: src/main.rs
fn foo(_: i32, y: i32) {
println!("This code only uses the y parameter: {y}");
}
@@ -505,7 +513,8 @@ parameters, as shown in Listing 18-17.
fn main() {
foo(3, 4);
}
-
Listing 18-17: Using _ in a function signature
+Listing 19-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.
In most cases when you no longer need a particular function parameter, you
@@ -518,10 +527,11 @@ would if you used a name instead.
We can also use _ inside another pattern to ignore just part of a value, for
example, when we want to test for only part of a value but have no use for the
-other parts in the corresponding code we want to run. Listing 18-18 shows code
+other parts in the corresponding code we want to run. Listing 19-18 shows code
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.
+
fn main() {
let mut setting_value = Some(5);
let new_setting_value = Some(10);
@@ -537,9 +547,8 @@ setting but can unset the setting and give it a value if it is currently unset.<
println!("setting is {setting_value:?}");
}
-
Listing 18-18: Using an underscore within patterns that
-match Some variants when we don’t need to use the value inside the
-Some
+Listing 19-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
the values inside either Some variant, but we do need to test for the case
@@ -550,8 +559,9 @@ changed.
None) expressed by the _ pattern in the second arm, we want to allow
new_setting_value to become setting_value.
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
+particular values. Listing 19-19 shows an example of ignoring the second and
fourth values in a tuple of five items.
+
fn main() {
let numbers = (2, 4, 8, 16, 32);
@@ -561,7 +571,8 @@ fourth values in a tuple of five items.
}
}
}
-
Listing 18-19: Ignoring multiple parts of a tuple
+Listing 19-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.
@@ -570,21 +581,23 @@ warning because an unused variable could be a bug. However, sometimes it’s
useful to be able to create a variable you won’t use yet, such as when you’re
prototyping or just starting a project. In this situation, you can tell Rust
not to warn you about the unused variable by starting the name of the variable
-with an underscore. In Listing 18-20, we create two unused variables, but when
+with an underscore. In Listing 19-20, we create two unused variables, but when
we compile this code, we should only get a warning about one of them.
-
Filename: src/main.rs
+
+Filename: src/main.rs
fn main() {
let _x = 5;
let y = 10;
}
-
Listing 18-20: Starting a variable name with an
-underscore to avoid getting unused variable warnings
+Listing 19-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.
Note that there is a subtle difference between using only _ and using a name
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.
+distinction matters, Listing 19-21 will provide us with an error.
+
fn main() {
let s = Some(String::from("Hello!"));
@@ -594,12 +607,13 @@ distinction matters, Listing 18-21 will provide us with an error.
println!("{s:?}");
}
-
Listing 18-21: An unused variable starting with an
-underscore still binds the value, which might take ownership of the value
+Listing 19-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
+doesn’t ever bind to the value. Listing 19-22 will compile without any errors
because s doesn’t get moved into _.
+
fn main() {
let s = Some(String::from("Hello!"));
@@ -609,17 +623,18 @@ because s doesn’t get moved into _.
println!("{s:?}");
}
-
Listing 18-22: Using an underscore does not bind the
-value
+Listing 19-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.
With values that have many parts, we can use the .. syntax to use specific
parts and ignore the rest, avoiding the need to list underscores for each
ignored value. The .. pattern ignores any parts of a value that we haven’t
-explicitly matched in the rest of the pattern. In Listing 18-23, we have a
+explicitly matched in the rest of the pattern. In Listing 19-23, we have a
Point struct that holds a coordinate in three-dimensional space. In the
match expression, we want to operate only on the x coordinate and ignore
the values in the y and z fields.
+
fn main() {
struct Point {
x: i32,
@@ -633,15 +648,16 @@ the values in the y and z fields.
Point { x, .. } => println!("x is {x}"),
}
}
-
Listing 18-23: Ignoring all fields of a Point except
-for x by using ..
+Listing 19-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
structs that have lots of fields in situations where only one or two fields are
relevant.
-
The syntax .. will expand to as many values as it needs to be. Listing 18-24
+
The syntax .. will expand to as many values as it needs to be. Listing 19-24
shows how to use .. with a tuple.
-
Filename: src/main.rs
+
+Filename: src/main.rs
fn main() {
let numbers = (2, 4, 8, 16, 32);
@@ -651,15 +667,16 @@ shows how to use .. with a tuple.
}
}
}
-
Listing 18-24: Matching only the first and last values in
-a tuple and ignoring all other values
+Listing 19-24: Matching only the first and last values in a tuple and ignoring all other values
+
In this code, the first and last value are matched with first and last. The
.. will match and ignore everything in the middle.
However, using .. must be unambiguous. If it is unclear which values are
intended for matching and which should be ignored, Rust will give us an error.
-Listing 18-25 shows an example of using .. ambiguously, so it will not
+Listing 19-25 shows an example of using .. ambiguously, so it will not
compile.
Listing 18-25: An attempt to use .. in an ambiguous
-way
+Listing 19-25: An attempt to use .. in an ambiguous way
+
When we compile this example, we get this error:
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
@@ -695,9 +712,10 @@ compiler error because using .. in two places like this is ambiguou
A match guard is an additional if condition, specified after the pattern in
a match arm, that must also match for that arm to be chosen. Match guards are
useful for expressing more complex ideas than a pattern alone allows.
-
The condition can use variables created in the pattern. Listing 18-26 shows a
+
The condition can use variables created in the pattern. Listing 19-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).
+
fn main() {
let num = Some(4);
@@ -707,7 +725,8 @@ guard of if x % 2 == 0 (which will be true if the number is even).<
None => (),
}
}
-
Listing 18-26: Adding a match guard to a pattern
+Listing 19-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
the match guard checks whether the remainder of dividing x by 2 is equal to
@@ -720,13 +739,14 @@ second arm doesn’t have a match guard and therefore matches any Some
-
In Listing 18-11, we mentioned that we could use match guards to solve our
+
In Listing 19-11, we mentioned that we could use match guards to solve our
pattern-shadowing problem. Recall that we created a new variable inside the
pattern in the match expression instead of using the variable outside the
match. That new variable meant we couldn’t test against the value of the
-outer variable. Listing 18-27 shows how we can use a match guard to fix this
+outer variable. Listing 19-27 shows how we can use a match guard to fix this
problem.
-
Filename: src/main.rs
+
+Filename: src/main.rs
fn main() {
let x = Some(5);
let y = 10;
@@ -739,8 +759,8 @@ problem.
println!("at the end: x = {x:?}, y = {y}");
}
-
Listing 18-27: Using a match guard to test for equality
-with an outer variable
+Listing 19-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,
meaning we can use the outer y in the match guard. Instead of specifying the
@@ -753,10 +773,11 @@ we can look for a value that has the same value as the outer y by c
n to y.
You can also use the or operator | in a match guard to specify multiple
patterns; the match guard condition will apply to all the patterns. Listing
-18-28 shows the precedence when combining a pattern that uses | with a match
+19-28 shows the precedence when combining a pattern that uses | with a match
guard. The important part of this example is that the if y match guard
applies to 4, 5, and6, even though it might look like if y only
applies to 6.
+
fn main() {
let x = 4;
let y = false;
@@ -766,8 +787,8 @@ applies to 6.
_ => println!("no"),
}
}
-
Listing 18-28: Combining multiple patterns with a match
-guard
+Listing 19-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 6and if y is true. When this code runs, the
pattern of the first arm matches because x is 4, but the match guard if y
@@ -787,11 +808,12 @@ were applied only to the final value in the list of values specified using the
yes.
The at operator @ lets us create a variable that holds a value at the same
-time as we’re testing that value for a pattern match. In Listing 18-29, we want
+time as we’re testing that value for a pattern match. In Listing 19-29, we want
to test that a Message::Helloid field is within the range 3..=7. We also
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.
+
fn main() {
enum Message {
Hello { id: i32 },
@@ -809,8 +831,8 @@ field, but for this example we’ll use a different name.
Message::Hello { id } => println!("Found some other id: {id}"),
}
}
-
Listing 18-29: Using @ to bind to a value in a pattern
-while also testing it
+Listing 19-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
while also testing that the value matched the range pattern.
In the second arm, where we only have a range specified in the pattern, the code
diff --git a/ch20-00-advanced-features.html b/ch20-00-advanced-features.html
index 39ffecec1..d8c28806f 100644
--- a/ch20-00-advanced-features.html
+++ b/ch20-00-advanced-features.html
@@ -182,7 +182,7 @@
By now, you’ve learned the most commonly used parts of the Rust programming
-language. Before we do one more project in Chapter 20, we’ll look at a few
+language. Before we do one more project in Chapter 21, we’ll look at a few
aspects of the language you might run into every once in a while, but may not
use every day. You can use this chapter as a reference for when you encounter
any unknowns. The features covered here are useful in very specific situations.
diff --git a/ch20-01-unsafe-rust.html b/ch20-01-unsafe-rust.html
index a26e17804..5f2a31428 100644
--- a/ch20-01-unsafe-rust.html
+++ b/ch20-01-unsafe-rust.html
@@ -257,15 +257,17 @@ mutable pointers or multiple mutable pointers to the same location
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.
-
Listing 19-1 shows how to create an immutable and a mutable raw pointer from
+
Listing 20-1 shows how to create an immutable and a mutable raw pointer from
references.
+
fn main() {
let mut num = 5;
let r1 = &num as *const i32;
let r2 = &mut num as *mut i32;
}
-
Listing 19-1: Creating raw pointers from references
+Listing 20-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
unsafe block, as you’ll see in a bit.
@@ -275,21 +277,23 @@ directly from references guaranteed to be valid, we know these particular raw
pointers are valid, but we can’t make that assumption about just any raw
pointer.
To demonstrate this, next we’ll create a raw pointer whose validity we can’t be
-so certain of. Listing 19-2 shows how to create a raw pointer to an arbitrary
+so certain of. Listing 20-2 shows how to create a raw pointer to an arbitrary
location in memory. Trying to use arbitrary memory is undefined: there might be
data at that address or there might not, the compiler might optimize the code
so there is no memory access, or the program might error with a segmentation
fault. Usually, there is no good reason to write code like this, but it is
possible.
+
fn main() {
let address = 0x012345usize;
let r = address as *const i32;
}
-
Listing 19-2: Creating a raw pointer to an arbitrary
-memory address
+Listing 20-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
+raw pointers and read the data being pointed to. In Listing 20-3, we use the
dereference operator * on a raw pointer that requires an unsafe block.
+
fn main() {
let mut num = 5;
@@ -301,11 +305,11 @@ dereference operator * on a raw pointer that requires an unsa
println!("r2 is: {}", *r2);
}
}
-
Listing 19-3: Dereferencing raw pointers within an
-unsafe block
+Listing 20-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.
-
Note also that in Listing 19-1 and 19-3, we created *const i32 and *mut i32
+
Note also that in Listing 20-1 and 20-3, we created *const i32 and *mut i32
raw pointers that both pointed to the same memory location, where num is
stored. If we instead tried to create an immutable and a mutable reference to
num, the code would not have compiled because Rust’s ownership rules don’t
@@ -366,7 +370,8 @@ a common abstraction. As an example, let’s study the split_at_mut
from the standard library, which requires some unsafe code. We’ll explore how
we might implement it. This safe method is defined on mutable slices: it takes
one slice and makes it two by splitting the slice at the index given as an
-argument. Listing 19-4 shows how to use split_at_mut.
+argument. Listing 20-4 shows how to use split_at_mut.
+
fn main() {
let mut v = vec![1, 2, 3, 4, 5, 6];
@@ -377,12 +382,13 @@ argument. Listing 19-4 shows how to use split_at_mut.
assert_eq!(a, &mut [1, 2, 3]);
assert_eq!(b, &mut [4, 5, 6]);
}
-
Listing 19-4: Using the safe split_at_mut
-function
+Listing 20-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
+something like Listing 20-5, which won’t compile. For simplicity, we’ll
implement split_at_mut as a function rather than a method and only for slices
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();
@@ -395,8 +401,8 @@ of i32 values rather than for a generic type T.
let mut vector = vec![1, 2, 3, 4, 5, 6];
let (left, right) = split_at_mut(&mut vector, 3);
}
-
Listing 19-5: An attempted implementation of
-split_at_mut using only safe Rust
+Listing 20-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
less than or equal to the length. The assertion means that if we pass an index
@@ -405,7 +411,7 @@ before it attempts to use that index.
Then we return two mutable slices in a tuple: one from the start of the
original slice to the mid index and another from mid to the end of the
slice.
-
When we try to compile the code in Listing 19-5, we’ll get an error.
+
When we try to compile the code in Listing 20-5, we’ll get an error.
$ cargo run
Compiling unsafe-example v0.1.0 (file:///projects/unsafe-example)
error[E0499]: cannot borrow `*values` as mutable more than once at a time
@@ -431,8 +437,9 @@ the slice; it only knows that we’re borrowing from the same slice twice.
Borrowing different parts of a slice is fundamentally okay because the two
slices aren’t overlapping, but Rust isn’t smart enough to know this. When we
know code is okay, but Rust doesn’t, it’s time to reach for unsafe code.
-
Listing 19-6 shows how to use an unsafe block, a raw pointer, and some calls
+
Listing 20-6 shows how to use an unsafe block, a raw pointer, and some calls
to unsafe functions to make the implementation of split_at_mut work.
Listing 19-6: Using unsafe code in the implementation of
-the split_at_mut function
+Listing 20-6: Using unsafe code in the implementation of the split_at_mut function
+
Recall from “The Slice Type” section in
Chapter 4 that slices are a pointer to some data and the length of the slice.
We use the len method to get the length of a slice and the as_mut_ptr
@@ -482,9 +489,10 @@ appropriate use of unsafe.
abstraction to the unsafe code with an implementation of the function that uses
unsafe code in a safe way, because it creates only valid pointers from the
data this function has access to.
-
In contrast, the use of slice::from_raw_parts_mut in Listing 19-7 would
+
In contrast, the use of slice::from_raw_parts_mut in Listing 20-7 would
likely crash when the slice is used. This code takes an arbitrary memory
location and creates a slice 10,000 items long.
+
fn main() {
use std::slice;
@@ -493,8 +501,8 @@ location and creates a slice 10,000 items long.
let values: &[i32] = unsafe { slice::from_raw_parts_mut(r, 10000) };
}
-
Listing 19-7: Creating a slice from an arbitrary memory
-location
+Listing 20-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
values as though it’s a valid slice results in undefined behavior.
@@ -504,12 +512,13 @@ language. For this, Rust has the keyword extern that facilitates th
and use of a Foreign Function Interface (FFI). An FFI is a way for a
programming language to define functions and enable a different (foreign)
programming language to call those functions.
-
Listing 19-8 demonstrates how to set up an integration with the abs function
+
Listing 20-8 demonstrates how to set up an integration with the abs function
from the C standard library. Functions declared within extern blocks are
always unsafe to call from Rust code. The reason is that other languages don’t
enforce Rust’s rules and guarantees, and Rust can’t check them, so
responsibility falls on the programmer to ensure safety.
-
Filename: src/main.rs
+
+Filename: src/main.rs
extern "C" {
fn abs(input: i32) -> i32;
}
@@ -519,8 +528,8 @@ fn main() {
println!("Absolute value of -3 according to C: {}", abs(-3));
}
}
-
Listing 19-8: Declaring and calling an extern function
-defined in another language
+Listing 20-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
application binary interface (ABI) the external function uses: the ABI
@@ -553,17 +562,18 @@ pub extern "C" fn call_from_c() {
In this book, we’ve not yet talked about global variables, which Rust does
support but can be problematic with Rust’s ownership rules. If two threads are
accessing the same mutable global variable, it can cause a data race.
-
In Rust, global variables are called static variables. Listing 19-9 shows an
+
In Rust, global variables are called static variables. Listing 20-9 shows an
example declaration and use of a static variable with a string slice as a
value.
Listing 19-9: Defining and using an immutable static
-variable
+Listing 20-9: Defining and using an immutable static variable
+
Static variables are similar to constants, which we discussed in the
“Differences Between Variables and
Constants” section
@@ -577,9 +587,10 @@ values in a static variable have a fixed address in memory. Using the value
will always access the same data. Constants, on the other hand, are allowed to
duplicate their data whenever they’re used. Another difference is that static
variables can be mutable. Accessing and modifying mutable static variables is
-unsafe. Listing 19-10 shows how to declare, access, and modify a mutable
+unsafe. Listing 20-10 shows how to declare, access, and modify a mutable
static variable named COUNTER.
Listing 19-10: Reading from or writing to a mutable
-static variable is unsafe
+Listing 20-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
code compiles and prints COUNTER: 3 as we would expect because it’s single
@@ -612,7 +623,8 @@ that data accessed from different threads is done safely.
least one of its methods has some invariant that the compiler can’t verify. We
declare that a trait is unsafe by adding the unsafe keyword before trait
and marking the implementation of the trait as unsafe too, as shown in
-Listing 19-11.
+Listing 20-11.
+
unsafe trait Foo {
// methods go here
}
@@ -622,8 +634,8 @@ unsafe impl Foo for i32 {
}
fn main() {}
-
Listing 19-11: Defining and implementing an unsafe
-trait
+Listing 20-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 Sync and Send marker traits we discussed in the
diff --git a/ch20-03-advanced-traits.html b/ch20-03-advanced-traits.html
index 6a95a089d..5f1477a62 100644
--- a/ch20-03-advanced-traits.html
+++ b/ch20-03-advanced-traits.html
@@ -200,14 +200,15 @@ the other features discussed in this chapter.
standard library provides. The associated type is named Item and stands in
for the type of the values the type implementing the Iterator trait is
iterating over. The definition of the Iterator trait is as shown in Listing
-19-12.
+20-12.
+
Listing 19-12: The definition of the Iterator trait
-that has an associated type Item
+Listing 20-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<Self::Item>. Implementors of the
Iterator trait will specify the concrete type for Item, and the next
@@ -217,7 +218,8 @@ latter allow us to define a function without specifying what types it can
handle. To examine the difference between the two concepts, we’ll look at an
implementation of the Iterator trait on a type named Counter that specifies
the Item type is u32:
-
Filename: src/lib.rs
+
+Filename: src/lib.rs
struct Counter {
count: u32,
}
@@ -241,14 +243,16 @@ the Item type is u32:
}
}
}
+
This syntax seems comparable to that of generics. So why not just define the
-Iterator trait with generics, as shown in Listing 19-13?
+Iterator trait with generics, as shown in Listing 20-13?
+
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
+Listing A hypothetical definition of the `Iterator` trait using generics
+
+
The difference is that when using generics, as in Listing 20-13, we must
annotate the types in each implementation; because we can also implement
Iterator<String> for Counter or any other type, we could have multiple
implementations of Iterator for Counter. In other words, when a trait has a
@@ -257,7 +261,7 @@ the concrete types of the generic type parameters each time. When we use the
next method on Counter, we would have to provide type annotations to
indicate which implementation of Iterator we want to use.
With associated types, we don’t need to annotate types because we can’t
-implement a trait on a type multiple times. In Listing 19-12 with the
+implement a trait on a type multiple times. In Listing 20-12 with the
definition that uses associated types, we can only choose what the type of
Item will be once, because there can only be one impl Iterator for Counter.
We don’t have to specify that we want an iterator of u32 values everywhere
@@ -277,10 +281,11 @@ in particular situations.
Rust doesn’t allow you to create your own operators or overload arbitrary
operators. But you can overload the operations and corresponding traits listed
in std::ops by implementing the traits associated with the operator. For
-example, in Listing 19-14 we overload the + operator to add two Point
+example, in Listing 20-14 we overload the + operator to add two Point
instances together. We do this by implementing the Add trait on a Point
struct:
Listing 19-14: Implementing the Add trait to overload
-the + operator for Point instances
+Listing 20-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
associated type named Output that determines the type returned from the add
@@ -339,8 +344,9 @@ units. This thin wrapping of an existing type in another struct is known as the
Pattern to Implement External Traits on External Types” section. We want to add values in millimeters to values in meters and have
the implementation of Add do the conversion correctly. We can implement Add
-for Millimeters with Meters as the Rhs, as shown in Listing 19-15.
-
Filename: src/lib.rs
+for Millimeters with Meters as the Rhs, as shown in Listing 20-15.
+
+Filename: src/lib.rs
use std::ops::Add;
struct Millimeters(u32);
@@ -353,8 +359,8 @@ impl Add<Meters> for Millimeters {
Millimeters(self.0 + (other.0 * 1000))
}
}
-
Listing 19-15: Implementing the Add trait on
-Millimeters to add Millimeters to Meters
+Listing 20-15: Implementing the Add trait on Millimeters to add Millimeters to Meters
+
To add Millimeters and Meters, we specify impl Add<Meters> to set the
value of the Rhs type parameter instead of using the default of Self.
You’ll use default type parameters in two main ways:
@@ -378,11 +384,12 @@ another trait’s method, nor does Rust prevent you from implementing both trait
on one type. It’s also possible to implement a method directly on the type with
the same name as methods from traits.
When calling methods with the same name, you’ll need to tell Rust which one you
-want to use. Consider the code in Listing 19-16 where we’ve defined two traits,
+want to use. Consider the code in Listing 20-16 where we’ve defined two traits,
Pilot and Wizard, that both have a method called fly. We then implement
both traits on a type Human that already has a method named fly implemented
on it. Each fly method does something different.
-
Filename: src/main.rs
+
+Filename: src/main.rs
trait Pilot {
fn fly(&self);
}
@@ -412,12 +419,12 @@ impl Human {
}
fn main() {}
-
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
+Listing 20-16: Two traits are defined to have a method and are implemented on theHumantype, and aflymethod is implemented onHuman` 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.
-
Filename: src/main.rs
+the method that is directly implemented on the type, as shown in Listing 20-17.
+
+Filename: src/main.rs
trait Pilot {
fn fly(&self);
}
@@ -450,14 +457,15 @@ the method that is directly implemented on the type, as shown in Listing 19-17.<
let person = Human;
person.fly();
}
-
Listing 19-17: Calling fly on an instance of
-Human
+Listing 20-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.
To call the fly methods from either the Pilot trait or the Wizard trait,
we need to use more explicit syntax to specify which fly method we mean.
-Listing 19-18 demonstrates this syntax.
-
Filename: src/main.rs
+Listing 20-18 demonstrates this syntax.
+
+Filename: src/main.rs
trait Pilot {
fn fly(&self);
}
@@ -492,12 +500,12 @@ Listing 19-18 demonstrates this syntax.
Wizard::fly(&person);
person.fly();
}
-
Listing 19-18: Specifying which trait’s fly method we
-want to call
+Listing 20-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
Human::fly(&person), which is equivalent to the person.fly() that we used
-in Listing 19-18, but this is a bit longer to write if we don’t need to
+in Listing 20-18, but this is a bit longer to write if we don’t need to
disambiguate.
Running this code prints the following:
$ cargo run
@@ -514,12 +522,13 @@ trait to use based on the type of self.
However, associated functions that are not methods don’t have a self
parameter. When there are multiple types or traits that define non-method
functions with the same function name, Rust doesn’t always know which type you
-mean unless you use fully qualified syntax. For example, in Listing 19-19 we
+mean unless you use fully qualified syntax. For example, in Listing 20-19 we
create a trait for an animal shelter that wants to name all baby dogs Spot.
We make an Animal trait with an associated non-method function baby_name.
The Animal trait is implemented for the struct Dog, on which we also
provide an associated non-method function baby_name directly.
-
Filename: src/main.rs
+
+Filename: src/main.rs
trait Animal {
fn baby_name() -> String;
}
@@ -541,9 +550,8 @@ impl Animal for Dog {
fn main() {
println!("A baby dog is called a {}", Dog::baby_name());
}
-
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
+Listing 20-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
Animal, which describes characteristics that all animals have. Baby dogs are
@@ -560,9 +568,10 @@ A baby dog is called a Spot
This output isn’t what we wanted. We want to call the baby_name function that
is part of the Animal trait that we implemented on Dog so the code prints
A baby dog is called a puppy. The technique of specifying the trait name that
-we used in Listing 19-18 doesn’t help here; if we change main to the code in
-Listing 19-20, we’ll get a compilation error.
-
Filename: src/main.rs
+we used in Listing 20-18 doesn’t help here; if we change main to the code in
+Listing 20-20, we’ll get a compilation error.
+
+Filename: src/main.rs
trait Animal {
fn baby_name() -> String;
}
@@ -584,9 +593,8 @@ Listing 19-20, we’ll get a compilation error.
fn main() {
println!("A baby dog is called a {}", Animal::baby_name());
}
-
Listing 19-20: Attempting to call the baby_name
-function from the Animal trait, but Rust doesn’t know which implementation to
-use
+Listing 20-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
implementation of Animal::baby_name we want. We’ll get this compiler error:
@@ -611,9 +619,10 @@ error: could not compile `traits-example` (bin "traits-example") due to 1 previo
To disambiguate and tell Rust that we want to use the implementation of
Animal for Dog as opposed to the implementation of Animal for some other
-type, we need to use fully qualified syntax. Listing 19-21 demonstrates how to
+type, we need to use fully qualified syntax. Listing 20-21 demonstrates how to
use fully qualified syntax.
-
Filename: src/main.rs
+
+Filename: src/main.rs
trait Animal {
fn baby_name() -> String;
}
@@ -635,9 +644,8 @@ use fully qualified syntax.
fn main() {
println!("A baby dog is called a {}", <Dog as Animal>::baby_name());
}
-
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
+Listing 20-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
implemented on Dog by saying that we want to treat the Dog type as an
@@ -680,9 +688,10 @@ should print the following:
OutlinePrint trait will work only for types that also implement Display and
provide the functionality that OutlinePrint needs. We can do that in the
trait definition by specifying OutlinePrint: Display. This technique is
-similar to adding a trait bound to the trait. Listing 19-22 shows an
+similar to adding a trait bound to the trait. Listing 20-22 shows an
implementation of the OutlinePrint trait.
-
Listing 19-22: Implementing the OutlinePrint trait that
-requires the functionality from Display
+Listing 20-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
that implements Display. If we tried to use to_string without adding a
@@ -708,7 +717,8 @@ error saying that no method named to_string was found for the type
the current scope.
Let’s see what happens when we try to implement OutlinePrint on a type that
doesn’t implement Display, such as the Point struct:
-
Filename: src/main.rs
+
+Filename: src/main.rs
use std::fmt;
trait OutlinePrint: fmt::Display {
@@ -734,6 +744,7 @@ impl OutlinePrint for Point {}
let p = Point { x: 1, y: 3 };
p.outline_print();
}
+
We get an error saying that Display is required but not implemented:
$ cargo run
Compiling traits-example v0.1.0 (file:///projects/traits-example)
@@ -772,7 +783,8 @@ error: could not compile `traits-example` (bin "traits-example") due to 2 previo
To fix this, we implement Display on Point and satisfy the constraint that
OutlinePrint requires, like so:
-
Filename: src/main.rs
+
+Filename: src/main.rs
trait OutlinePrint: fmt::Display {
fn outline_print(&self) {
let output = self.to_string();
@@ -804,6 +816,7 @@ impl fmt::Display for Point {
let p = Point { x: 1, y: 3 };
p.outline_print();
}
+
Then implementing the OutlinePrint trait on Point will compile
successfully, and we can call outline_print on a Point instance to display
it within an outline of asterisks.
@@ -825,8 +838,9 @@ type is elided at compile time.
orphan rule prevents us from doing directly because the Display trait and the
Vec<T> type are defined outside our crate. We can make a Wrapper struct
that holds an instance of Vec<T>; then we can implement Display on
-Wrapper and use the Vec<T> value, as shown in Listing 19-23.
-
Filename: src/main.rs
+Wrapper and use the Vec<T> value, as shown in Listing 20-23.
+
+Filename: src/main.rs
use std::fmt;
struct Wrapper(Vec<String>);
@@ -841,8 +855,8 @@ fn main() {
let w = Wrapper(vec![String::from("hello"), String::from("world")]);
println!("w = {w}");
}
-
Listing 19-23: Creating a Wrapper type around
-Vec<String> to implement Display
+Listing 20-23: Creating a Wrapper type around Vec<String> to implement Display
+
The implementation of Display uses self.0 to access the inner Vec<T>,
because Wrapper is a tuple struct and Vec<T> is the item at index 0 in the
tuple. Then we can use the functionality of the Display trait on Wrapper.
The newtype pattern is also useful for tasks beyond those we’ve discussed so
far, including statically enforcing that values are never confused and
indicating the units of a value. You saw an example of using newtypes to
-indicate units in Listing 19-15: recall that the Millimeters and Meters
+indicate units in Listing 20-15: recall that the Millimeters and Meters
structs wrapped u32 values in a newtype. If we wrote a function with a
parameter of type Millimeters, we couldn’t compile a program that
accidentally tried to call that function with a value of type Meters or a
@@ -212,7 +212,7 @@ internally. The newtype pattern is a lightweight way to achieve encapsulation
to hide implementation details, which we discussed in the “Encapsulation that
Hides Implementation
Details”
-section of Chapter 17.
Rust provides the ability to declare a type alias to give an existing type
another name. For this we use the type keyword. For example, we can create
@@ -226,7 +226,7 @@ the alias Kilometers to i32 like so:
println!("x + y = {}", x + y);
}
Now, the alias Kilometers is a synonym for i32; unlike the Millimeters
-and Meters types we created in Listing 19-15, Kilometers is not a separate,
+and Meters types we created in Listing 20-15, Kilometers is not a separate,
new type. Values that have the type Kilometers will be treated the same as
values of type i32:
fn main() {
@@ -248,7 +248,8 @@ might have a lengthy type like this:
Box<dyn Fn() + Send + 'static>
Writing this lengthy type in function signatures and as type annotations all
over the code can be tiresome and error prone. Imagine having a project full of
-code like that in Listing 19-24.
+code like that in Listing 20-24.
+
fn main() {
let f: Box<dyn Fn() + Send + 'static> = Box::new(|| println!("hi"));
@@ -261,10 +262,12 @@ code like that in Listing 19-24.
Box::new(|| ())
}
}
-
Listing 19-24: Using a long type in many places
+Listing 20-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
+Listing 20-25, we’ve introduced an alias named Thunk for the verbose type and
can replace all uses of the type with the shorter alias Thunk.
+
fn main() {
type Thunk = Box<dyn Fn() + Send + 'static>;
@@ -279,8 +282,8 @@ can replace all uses of the type with the shorter alias Thunk.
Box::new(|| ())
}
}
-
Listing 19-25: Introducing a type alias Thunk to reduce
-repetition
+Listing 20-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
to be evaluated at a later time, so it’s an appropriate name for a closure that
@@ -348,7 +351,8 @@ never are called diverging functions. We can’t create values of the t
so bar can never possibly return.
But what use is a type you can never create values for? Recall the code from
Listing 2-5, part of the number guessing game; we’ve reproduced a bit of it
-here in Listing 19-26.
+here in Listing 20-26.
+
use rand::Rng;
use std::cmp::Ordering;
use std::io;
@@ -390,8 +394,8 @@ here in Listing 19-26.
}
}
}
-
Listing 19-26: A match with an arm that ends in
-continue
+Listing 20-26: A match with an arm that ends in continue
+
At the time, we skipped over some details in this code. In Chapter 6 in “The
match Control Flow Operator”
section, we discussed that match arms must all return the same type. So, for
@@ -406,7 +410,7 @@ example, the following code doesn’t work:
The type of guess in this code would have to be an integer and a string,
and Rust requires that guess have only one type. So what does continue
return? How were we allowed to return a u32 from one arm and have another arm
-that ends with continue in Listing 19-26?
+that ends with continue in Listing 20-26?
As you might have guessed, continue has a ! value. That is, when Rust
computes the type of guess, it looks at both match arms, the former with a
value of u32 and the latter with a ! value. Because ! can never have a
@@ -434,7 +438,7 @@ this definition:
}
}
}
-
In this code, the same thing happens as in the match in Listing 19-26: Rust
+
In this code, the same thing happens as in the match in Listing 20-26: Rust
sees that val has the type T and panic! has the type !, so the result
of the overall match expression is T. This code works because panic!
doesn’t produce a value; it ends the program. In the None case, we won’t be
@@ -487,7 +491,7 @@ types behind a pointer of some kind.
We can combine str with all kinds of pointers: for example, Box<str> or
Rc<str>. In fact, you’ve seen this before but with a different dynamically
sized type: traits. Every trait is a dynamically sized type we can refer to by
-using the name of the trait. In Chapter 17 in the “Using Trait Objects That
+using the name of the trait. In Chapter 18 in the “Using Trait Objects That
Allow for Values of Different
Types” section, we mentioned that to use traits as trait objects, we must
diff --git a/ch20-05-advanced-functions-and-closures.html b/ch20-05-advanced-functions-and-closures.html
index 8a1eaddcf..010c8bbd4 100644
--- a/ch20-05-advanced-functions-and-closures.html
+++ b/ch20-05-advanced-functions-and-closures.html
@@ -192,14 +192,15 @@ closure trait. The fn type is called a function pointer. P
with function pointers will allow you to use functions as arguments to other
functions.
The syntax for specifying that a parameter is a function pointer is similar to
-that of closures, as shown in Listing 19-27, where we’ve defined a function
+that of closures, as shown in Listing 20-27, where we’ve defined a function
add_one that adds one to its parameter. The function do_twice takes two
parameters: a function pointer to any function that takes an i32 parameter
and returns an i32, and one i32 value. The do_twice function calls the
function f twice, passing it the arg value, then adds the two function call
results together. The main function calls do_twice with the arguments
add_one and 5.
Listing 19-27: Using the fn type to accept a function
-pointer as an argument
+Listing 20-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
i32. We can then call f in the body of do_twice. In main, we can pass
@@ -310,7 +311,7 @@ We can use a trait object:
We could also use the vec! macro to make a vector of two integers or a vector
of five string slices. We wouldn’t be able to use a function to do the same
because we wouldn’t know the number or type of values up front.
-
Listing 19-28 shows a slightly simplified definition of the vec! macro.
-
Filename: src/lib.rs
+
Listing 20-28 shows a slightly simplified definition of the vec! macro.
Listing 19-28: A simplified version of the vec! macro
-definition
+Listing 20-28: A simplified version of the vec! macro definition
+
Note: The actual definition of the vec! macro in the standard library
includes code to preallocate the correct amount of memory up front. That code
@@ -278,9 +279,9 @@ is the only pattern in this macro, there is only one valid way to match; any
other pattern will result in an error. More complex macros will have more than
one arm.
Valid pattern syntax in macro definitions is different than the pattern syntax
-covered in Chapter 18 because macro patterns are matched against Rust code
+covered in Chapter 19 because macro patterns are matched against Rust code
structure rather than values. Let’s walk through what the pattern pieces in
-Listing 19-28 mean; for the full macro pattern syntax, see the Rust
+Listing 20-28 mean; for the full macro pattern syntax, see the Rust
Reference.
First, we use a set of parentheses to encompass the whole pattern. We use a
dollar sign ($) to declare a variable in the macro system that will contain
@@ -321,17 +322,18 @@ macros do. The three kinds of procedural macros are custom derive,
attribute-like, and function-like, and all work in a similar fashion.
When creating procedural macros, the definitions must reside in their own crate
with a special crate type. This is for complex technical reasons that we hope
-to eliminate in the future. In Listing 19-29, we show how to define a
+to eliminate in the future. In Listing 20-29, we show how to define a
procedural macro, where some_attribute is a placeholder for using a specific
macro variety.
Listing 19-29: An example of defining a procedural
-macro
+Listing 20-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
the proc_macro crate that is included with Rust and represents a sequence of
@@ -351,8 +353,9 @@ we’ll provide a procedural macro so users can annotate their type with
#[derive(HelloMacro)] to get a default implementation of the hello_macro
function. The default implementation will print Hello, Macro! My name is TypeName! where TypeName is the name of the type on which this trait has
been defined. In other words, we’ll write a crate that enables another
-programmer to write code like Listing 19-30 using our crate.
-
Filename: src/main.rs
+programmer to write code like Listing 20-30 using our crate.
+
+Filename: src/main.rs
use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;
@@ -362,17 +365,19 @@ struct Pancakes;
fn main() {
Pancakes::hello_macro();
}
-
Listing 19-30: The code a user of our crate will be able
-to write when using our procedural macro
+Listing 20-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:
$ cargo new hello_macro --lib
Next, we’ll define the HelloMacro trait and its associated function:
-
Filename: src/lib.rs
+
+Filename: src/lib.rs
pub trait HelloMacro {
fn hello_macro();
}
+
We have a trait and its function. At this point, our crate user could implement
the trait to achieve the desired functionality, like so:
use hello_macro::HelloMacro;
@@ -417,7 +422,8 @@ possible for programmers to use hello_macro even if they don’t wa
We’ll also need functionality from the syn and quote crates, as you’ll see
in a moment, so we need to add them as dependencies. Add the following to the
Cargo.toml file for hello_macro_derive:
-
To start defining the procedural macro, place the code in Listing 19-31 into
+
+
To start defining the procedural macro, place the code in Listing 20-31 into
your src/lib.rs file for the hello_macro_derive crate. Note that this code
won’t compile until we add a definition for the impl_hello_macro function.
-
Filename: hello_macro_derive/src/lib.rs
+
+Filename: hello_macro_derive/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
@@ -441,8 +449,8 @@ pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
// Build the trait implementation
impl_hello_macro(&ast)
}
-
Listing 19-31: Code that most procedural macro crates
-will require in order to process Rust code
+Listing 20-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
function, which is responsible for transforming the syntax tree: this makes
@@ -469,8 +477,9 @@ convention most procedural macros follow.
TokenStream to a data structure that we can then interpret and perform
operations on. This is where syn comes into play. The parse function in
syn takes a TokenStream and returns a DeriveInput struct representing the
-parsed Rust code. Listing 19-32 shows the relevant parts of the DeriveInput
+parsed Rust code. Listing 20-32 shows the relevant parts of the DeriveInput
struct we get from parsing the struct Pancakes; string:
+
DeriveInput {
// --snip--
@@ -488,8 +497,8 @@ struct we get from parsing the struct Pancakes; string:
}
)
}
-
Listing 19-32: The DeriveInput instance we get when
-parsing the code that has the macro’s attribute in Listing 19-30
+Listing 20-32: The DeriveInput instance we get when parsing the code that has the macro’s attribute in Listing 20-30
+
Now that we have the code to turn the annotated Rust code from a TokenStream
into a DeriveInput instance, let’s generate the code that implements the
-HelloMacro trait on the annotated type, as shown in Listing 19-33.
-
Filename: hello_macro_derive/src/lib.rs
+HelloMacro trait on the annotated type, as shown in Listing 20-33.
+
+Filename: hello_macro_derive/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
@@ -535,15 +545,15 @@ into a DeriveInput instance, let’s generate the code that impleme
};
gen.into()
}
-
Listing 19-33: Implementing the HelloMacro trait using
-the parsed Rust code
+Listing 20-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
-we run the impl_hello_macro function on the code in Listing 19-30, the
+annotated type using ast.ident. The struct in Listing 20-32 shows that when
+we run the impl_hello_macro function on the code in Listing 20-30, the
ident we get will have the ident field with a value of "Pancakes". Thus,
-the name variable in Listing 19-33 will contain an Ident struct instance
+the name variable in Listing 20-33 will contain an Ident struct instance
that, when printed, will be the string "Pancakes", the name of the struct in
-Listing 19-30.
+Listing 20-30.
The quote! macro lets us define the Rust code that we want to return. The
compiler expects something different to the direct result of the quote!
macro’s execution, so we need to convert it to a TokenStream. We do this by
@@ -567,7 +577,7 @@ expression to print literally, so we use stringify!. Using st
saves an allocation by converting #name to a string literal at compile time.
At this point, cargo build should complete successfully in both hello_macro
and hello_macro_derive. Let’s hook up these crates to the code in Listing
-19-30 to see the procedural macro in action! Create a new binary project in
+20-30 to see the procedural macro in action! Create a new binary project in
your projects directory using cargo new pancakes. We need to add
hello_macro and hello_macro_derive as dependencies in the pancakes
crate’s Cargo.toml. If you’re publishing your versions of hello_macro and
@@ -576,7 +586,7 @@ dependencies; if not, you can specify them as path dependencies as
Put the code in Listing 19-30 into src/main.rs, and run cargo run: it
+
Put the code in Listing 20-30 into src/main.rs, and run cargo run: it
should print Hello, Macro! My name is Pancakes! The implementation of the
HelloMacro trait from the procedural macro was included without the
pancakes crate needing to implement it; the #[derive(HelloMacro)] added the
diff --git a/ch21-01-single-threaded.html b/ch21-01-single-threaded.html
index 825688e6e..c3d9b5e04 100644
--- a/ch21-01-single-threaded.html
+++ b/ch21-01-single-threaded.html
@@ -204,10 +204,11 @@ this. Let’s make a new project in the usual fashion:
Created binary (application) `hello` project
$ cd hello
-
Now enter the code in Listing 20-1 in src/main.rs to start. This code will
+
Now enter the code in Listing 21-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!.
Listing 20-1: Listening for incoming streams and printing
-a message when we receive a stream
+Listing 21-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. In the address, the section before the colon is an IP address
representing your computer (this is the same on every computer and doesn’t
@@ -291,8 +292,9 @@ separate the concerns of first getting a connection and then taking some action
with the connection, we’ll start a new function for processing connections. In
this new handle_connection function, we’ll read data from the TCP stream and
print it so we can see the data being sent from the browser. Change the code to
-look like Listing 20-2.
Listing 20-2: Reading from the TcpStream and printing
-the data
+Listing 21-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. In the for
loop in the main function, instead of printing a message that says we made a
@@ -425,8 +427,9 @@ response.
successful HTTP response. Let’s write this to the stream as our response to a
successful request! From the handle_connection function, remove the
println! that was printing the request data and replace it with the code in
-Listing 20-3.
-
Listing 20-3: Writing a tiny successful HTTP response to
-the stream
+Listing 21-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. Then we call as_bytes on our response to convert the string
data to bytes. The write_all method on stream takes a &[u8] and sends
@@ -470,9 +473,10 @@ request and sending a response!
Let’s implement the functionality for returning more than a blank page. Create
the new file hello.html in the root of your project directory, not in the
-src directory. You can input any HTML you want; Listing 20-4 shows one
+src directory. You can input any HTML you want; Listing 21-4 shows one
possibility.
Listing 20-4: A sample HTML file to return in a
-response
+Listing 21-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
-shown in Listing 20-5 to read the HTML file, add it to the response as a body,
+shown in Listing 21-5 to read the HTML file, add it to the response as a body,
and send it.
-
Filename: src/main.rs
+
+Filename: src/main.rs
use std::{
fs,
io::{prelude::*, BufReader},
@@ -526,8 +531,8 @@ and send it.
stream.write_all(response.as_bytes()).unwrap();
}
-
Listing 20-5: Sending the contents of hello.html as the
-body of the response
+Listing 21-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. The code for reading the contents of a file to a
string should look familiar; we used it in Chapter 12 when we read the contents
@@ -550,10 +555,11 @@ request to /.
client requested. Let’s add functionality to check that the browser is
requesting / before returning the HTML file and return an error if the
browser requests anything else. For this we need to modify handle_connection,
-as shown in Listing 20-6. This new code checks the content of the request
+as shown in Listing 21-6. This new code checks the content of the request
received against what we know a request for / looks like and adds if and
else blocks to treat requests differently.
-
Filename: src/main.rs
+
+Filename: src/main.rs
use std::{
fs,
io::{prelude::*, BufReader},
@@ -589,14 +595,14 @@ fn handle_connection(mut stream: TcpStream) {
// some other request
}
}
-
Listing 20-6: Handling requests to / differently from
-other requests
+Listing 21-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
first item from the iterator. The first unwrap takes care of the Option and
stops the program if the iterator has no items. The second unwrap handles the
Result and has the same effect as the unwrap that was in the map added in
-Listing 20-2.
+Listing 21-2.
Next, we check the request_line to see if it equals the request line of a GET
request to the / path. If it does, the if block returns the contents of our
HTML file.
@@ -606,12 +612,13 @@ a moment to respond to all other requests.
Run this code now and request 127.0.0.1:7878; you should get the HTML in
hello.html. If you make any other request, such as
127.0.0.1:7878/something-else, you’ll get a connection error like those you
-saw when running the code in Listing 20-1 and Listing 20-2.
-
Now let’s add the code in Listing 20-7 to the else block to return a response
+saw when running the code in Listing 21-1 and Listing 21-2.
+
Now let’s add the code in Listing 21-7 to the else block to return a response
with the status code 404, which signals that the content for the request was
not found. We’ll also return some HTML for a page to render in the browser
indicating the response to the end user.
-
Filename: src/main.rs
+
+Filename: src/main.rs
use std::{
fs,
io::{prelude::*, BufReader},
@@ -655,14 +662,15 @@ indicating the response to the end user.
stream.write_all(response.as_bytes()).unwrap();
}
}
-
Listing 20-7: Responding with status code 404 and an
-error page if anything other than / was requested
+Listing 21-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. The body of the response will be the HTML in the file 404.html.
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.
Listing 20-8: Sample content for the page to send back
-with any 404 response
+Listing 21-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
127.0.0.1:7878/foo, should return the error HTML from 404.html.
@@ -687,9 +695,10 @@ differences are the status line and the filename. Let’s make the code more
concise by pulling out those differences into separate if and else lines
that will assign the values of the status line and the filename to variables;
we can then use those variables unconditionally in the code to read the file
-and write the response. Listing 20-9 shows the resulting code after replacing
+and write the response. Listing 21-9 shows the resulting code after replacing
the large if and else blocks.
-
Listing 20-9: Refactoring the if and else blocks to
-contain only the code that differs between the two cases
+Listing 21-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
two values to status_line and filename using a pattern in the let
-statement, as discussed in Chapter 18.
+statement, as discussed in Chapter 19.
The previously duplicated code is now outside the if and else blocks and
uses the status_line and filename variables. This makes it easier to see
the difference between the two cases, and it means we have only one place to
update the code if we want to change how the file reading and response writing
-work. The behavior of the code in Listing 20-9 will be the same as that in
-Listing 20-7.
+work. The behavior of the code in Listing 21-9 will be the same as that in
+Listing 21-7.
Awesome! We now have a simple web server in approximately 40 lines of Rust code
that responds to one request with a page of content and responds to all other
requests with a 404 response.
diff --git a/ch21-02-multithreaded.html b/ch21-02-multithreaded.html
index b91c18eee..eff6750e3 100644
--- a/ch21-02-multithreaded.html
+++ b/ch21-02-multithreaded.html
@@ -190,10 +190,11 @@ finished, even if the new requests can be processed quickly. We’ll need to fix
this, but first, we’ll look at the problem in action.
We’ll look at how a slow-processing request can affect other requests made to
-our current server implementation. Listing 20-10 implements handling a request
+our current server implementation. Listing 21-10 implements handling a request
to /sleep with a simulated slow response that will cause the server to sleep
for 5 seconds before responding.
Listing 20-10: Simulating a slow request by sleeping for
-5 seconds
+Listing 21-10: Simulating a slow request by sleeping for 5 seconds
+
We switched from if to match now that we have three cases. We need to
explicitly match on a slice of request_line to pattern match against the
string literal values; match doesn’t do automatic referencing and
dereferencing like the equality method does.
-
The first arm is the same as the if block from Listing 20-9. The second arm
+
The first arm is the same as the if block from Listing 21-9. The second arm
matches a request to /sleep. When that request is received, the server will
sleep for 5 seconds before rendering the successful HTML page. The third arm is
-the same as the else block from Listing 20-9.
+the same as the else block from Listing 21-9.
You can see how primitive our server is: real libraries would handle the
recognition of multiple requests in a much less verbose way!
Start the server using cargo run. Then open two browser windows: one for
@@ -305,9 +306,10 @@ every connection. As mentioned earlier, this isn’t our final plan due to the
problems with potentially spawning an unlimited number of threads, but it is a
starting point to get a working multithreaded server first. Then we’ll add the
thread pool as an improvement, and contrasting the two solutions will be
-easier. Listing 20-11 shows the changes to make to main to spawn a new thread
+easier. Listing 21-11 shows the changes to make to main to spawn a new thread
to handle each stream within the for loop.
-
Filename: src/main.rs
+
+Filename: src/main.rs
use std::{
fs,
io::{prelude::*, BufReader},
@@ -349,8 +351,8 @@ to handle each stream within the for loop.
stream.write_all(response.as_bytes()).unwrap();
}
-
Listing 20-11: Spawning a new thread for each
-stream
+Listing 21-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
/sleep in your browser, then / in two more browser tabs, you’ll indeed see
@@ -362,9 +364,10 @@ new threads without any limit.
We want our thread pool to work in a similar, familiar way so switching from
threads to a thread pool doesn’t require large changes to the code that uses
-our API. Listing 20-12 shows the hypothetical interface for a ThreadPool
+our API. Listing 21-12 shows the hypothetical interface for a ThreadPool
struct we want to use instead of thread::spawn.
-
Filename: src/main.rs
+
+Filename: src/main.rs
use std::{
fs,
io::{prelude::*, BufReader},
@@ -407,7 +410,8 @@ struct we want to use instead of thread::spawn.
stream.write_all(response.as_bytes()).unwrap();
}
-
Listing 20-12: Our ideal ThreadPool interface
+Listing 21-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. Then, in the for loop, pool.execute has a
similar interface as thread::spawn in that it takes a closure the pool should
@@ -417,7 +421,7 @@ compile, but we’ll try so the compiler can guide us in how to fix it.
Make the changes in Listing 20-12 to src/main.rs, and then let’s use the
+
Make the changes in Listing 21-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:
$ cargo check
@@ -440,11 +444,14 @@ library for any work we want to do using a thread pool, not just for serving
web requests.
Create a src/lib.rs that contains the following, which is the simplest
definition of a ThreadPool struct that we can have for now:
-
Filename: src/lib.rs
+
+Filename: src/lib.rs
pub struct ThreadPool;
+
Then edit main.rs file to bring ThreadPool into scope from the library
crate by adding the following code to the top of src/main.rs:
-
Filename: src/main.rs
+
+Filename: src/main.rs
use hello::ThreadPool;
use std::{
fs,
@@ -488,6 +495,7 @@ crate by adding the following code to the top of src/main.rs:
stream.write_all(response.as_bytes()).unwrap();
}
+
This code still won’t work, but let’s check it again to get the next error that
we need to address:
$ cargo check
@@ -506,7 +514,8 @@ error: could not compile `hello` (bin "hello") due to 1 previous error
that can accept 4 as an argument and should return a ThreadPool instance.
Let’s implement the simplest new function that will have those
characteristics:
-
We chose usize as the type of the size parameter, because we know that a
negative number of threads doesn’t make any sense. We also know we’ll use this
4 as the number of elements in a collection of threads, which is what the
@@ -562,7 +572,8 @@ request’s closure one time, which matches the Once in FnOnc
closure from one thread to another and 'static because we don’t know how long
the thread will take to execute. Let’s create an execute method on
ThreadPool that will take a generic parameter of type F with these bounds:
-
Filename: src/lib.rs
+
+Filename: src/lib.rs
pub struct ThreadPool;
impl ThreadPool {
@@ -577,6 +588,7 @@ the thread will take to execute. Let’s create an execute method o
{
}
}
+
We still use the () after FnOnce because this FnOnce represents a closure
that takes no parameters and returns the unit type (). Just like function
definitions, the return type can be omitted from the signature, but even if we
@@ -607,8 +619,9 @@ parameter, because a pool with a negative number of threads makes no sense.
However, a pool with zero threads also makes no sense, yet zero is a perfectly
valid usize. We’ll add code to check that size is greater than zero before
we return a ThreadPool instance and have the program panic if it receives a
-zero by using the assert! macro, as shown in Listing 20-13.
-
Filename: src/lib.rs
+zero by using the assert! macro, as shown in Listing 21-13.
+
+Filename: src/lib.rs
pub struct ThreadPool;
impl ThreadPool {
@@ -632,8 +645,8 @@ zero by using the assert! macro, as shown in Listing 20-13.
{
}
}
-
Listing 20-13: Implementing ThreadPool::new to panic if
-size is zero
+Listing 21-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, as discussed in
@@ -660,12 +673,13 @@ look at the thread::spawn signature:
closure returns. Let’s try using JoinHandle too and see what happens. In our
case, the closures we’re passing to the thread pool will handle the connection
and not return anything, so T will be the unit type ().
-
The code in Listing 20-14 will compile but doesn’t create any threads yet.
+
The code in Listing 21-14 will compile but doesn’t create any threads yet.
We’ve changed the definition of ThreadPool to hold a vector of
thread::JoinHandle<()> instances, initialized the vector with a capacity of
size, set up a for loop that will run some code to create the threads, and
returned a ThreadPool instance containing them.
Listing 20-14: Creating a vector for ThreadPool to hold
-the threads
+Listing 21-14: Creating a vector for ThreadPool to hold the threads
+
We’ve brought std::thread into scope in the library crate, because we’re
using thread::JoinHandle as the type of the items in the vector in
ThreadPool.
@@ -713,7 +727,7 @@ this allocation up front is slightly more efficient than using Vec::new
When you run cargo check again, it should succeed.
We left a comment in the for loop in Listing 20-14 regarding the creation of
+
We left a comment in the for loop in Listing 21-14 regarding the creation of
threads. Here, we’ll look at how we actually create threads. The standard
library provides thread::spawn as a way to create threads, and
thread::spawn expects to get some code the thread should run as soon as the
@@ -747,9 +761,10 @@ closure.
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.
-
Ready? Here is Listing 20-15 with one way to make the preceding modifications.
-
Filename: src/lib.rs
+looking at the code in Listing 21-15.
+
Ready? Here is Listing 21-15 with one way to make the preceding modifications.
Listing 20-15: Modifying ThreadPool to hold Worker
-instances instead of holding threads directly
+Listing 21-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<()>
instances. We use the counter in the for loop as an argument to
@@ -842,10 +857,11 @@ sender.
closures of any jobs it receives.
Let’s start by creating a channel in ThreadPool::new and holding the sender
-in the ThreadPool instance, as shown in Listing 20-16. The Job struct
+in the ThreadPool instance, as shown in Listing 21-16. The Job struct
doesn’t hold anything for now but will be the type of item we’re sending down
the channel.
Listing 20-16: Modifying ThreadPool to store the
-sender of a channel that transmits Job instances
+Listing 21-16: Modifying ThreadPool to store the sender of a channel that transmits Job instances
+
In ThreadPool::new, we create our new channel and have the pool hold the
sender. This will successfully compile.
Let’s try passing a receiver of the channel into each worker as the thread pool
creates the channel. We know we want to use the receiver in the thread that the
workers spawn, so we’ll reference the receiver parameter in the closure. The
-code in Listing 20-17 won’t quite compile yet.
Listing 20-17: Passing the receiver to the workers
+Listing 21-17: Passing the receiver to the workers
+
We’ve made some small and straightforward changes: we pass the receiver into
Worker::new, and then we use it inside the closure.
When we try to check this code, we get this error:
@@ -1009,8 +1027,9 @@ otherwise, we might get race conditions (as covered in Chapter 16).
ownership across multiple threads and allow the threads to mutate the value, we
need to use Arc<Mutex<T>>. 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.
-
Filename: src/lib.rs
+receiver at a time. Listing 21-18 shows the changes we need to make.
+
+Filename: src/lib.rs
use std::{
sync::{mpsc, Arc, Mutex},
thread,
@@ -1075,8 +1094,8 @@ receiver at a time. Listing 20-18 shows the changes we need to make.
Worker { id, thread }
}
}
-
Listing 20-18: Sharing the receiver among the workers
-using Arc and Mutex
+Listing 21-18: Sharing the receiver among the workers using Arc and Mutex
+
In ThreadPool::new, we put the receiver in an Arc and a Mutex. For each
new worker, we clone the Arc to bump the reference count so the workers can
share ownership of the receiver.
@@ -1086,9 +1105,10 @@ share ownership of the receiver.
Job from a struct to a type alias for a trait object that holds the type of
closure that execute receives. As discussed in the “Creating Type Synonyms
with Type Aliases”
-section of Chapter 19, type aliases allow us to make long types shorter for
-ease of use. Look at Listing 20-19.
-
Filename: src/lib.rs
+section of Chapter 20, type aliases allow us to make long types shorter for
+ease of use. Look at Listing 21-19.
+
+Filename: src/lib.rs
Listing 20-19: Creating a Job type alias for a Box
-that holds each closure and then sending the job down the channel
+Listing 21-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, we
send that job down the sending end of the channel. We’re calling unwrap on
send for the case that sending fails. This might happen if, for example, we
@@ -1168,8 +1188,9 @@ compiler doesn’t know that.
thread::spawn still only references the receiving end of the channel.
Instead, we need the closure to loop forever, asking the receiving end of the
channel for a job and running the job when it gets one. Let’s make the change
-shown in Listing 20-20 to Worker::new.
-
Filename: src/lib.rs
+shown in Listing 21-20 to Worker::new.
+
+Filename: src/lib.rs
Listing 20-20: Receiving and executing the jobs in the
-worker’s thread
+Listing 21-20: Receiving and executing the jobs in the worker’s thread
+
Here, we first call lock on the receiver to acquire the mutex, and then we
call unwrap to panic on any errors. Acquiring a lock might fail if the mutex
is in a poisoned state, which can happen if some other thread panicked while
@@ -1255,7 +1276,7 @@ wait until a job becomes available. The Mutex<T> ensures that
Our thread pool is now in a working state! Give it a cargo run and make some
requests:
for all of the many
useful methods defined on Vec<T> by the standard library. For example, in
@@ -9033,7 +9037,7 @@ fn main() -> Result<(), Box<dyn Error>> {
allows the use of the ? operator on Result values.
The Box<dyn Error> type is a trait object, which we’ll talk about in the
“Using Trait Objects that Allow for Values of Different
-Types” section in Chapter 17. For now, you can
+Types” section in Chapter 18. For now, you can
read Box<dyn Error> to mean “any kind of error.” Using ? on a Result
value in a main function with the error type Box<dyn Error> is allowed
because it allows any Err value to be returned early. Even though the body of
@@ -9126,7 +9130,7 @@ format.
rather than checking for the problem at every step.
There’s not a good way to encode this information in the types you use. We’ll
work through an example of what we mean in the “Encoding States and Behavior
-as Types” section of Chapter 17.
+as Types” section of Chapter 18.
If someone calls your code and passes in values that don’t make sense, it’s
best to return an error if you can so the user of the library can decide what
@@ -10332,7 +10336,7 @@ around how the impl Trait syntax is implemented in the compiler. We
how to write a function with this behavior in the “Using Trait Objects That
Allow for Values of Different
Types” section of Chapter 17.
By using a trait bound with an impl block that uses generic type parameters,
we can implement methods conditionally for types that implement the specified
@@ -11104,7 +11108,7 @@ behavior the code needs. You learned how to use lifetime annotations to ensure
that this flexible code won’t have any dangling references. And all of this
analysis happens at compile time, which doesn’t affect runtime performance!
Believe it or not, there is much more to learn on the topics we discussed in
-this chapter: Chapter 17 discusses trait objects, which are another way to use
+this chapter: Chapter 18 discusses trait objects, which are another way to use
traits. There are also more complex scenarios involving lifetime annotations
that you will only need in very advanced scenarios; for those, you should read
the Rust Reference. But next, you’ll learn how to write tests in
@@ -12752,7 +12756,7 @@ background knowledge you need to understand a real-world project such as
Let’s create a new project with, as always, cargo new. We’ll call our project
@@ -13569,7 +13573,7 @@ returned the unit type, (), and we keep that as the value returned
Ok case.
For the error type, we used the trait objectBox<dyn Error> (and we’ve
brought std::error::Error into scope with a use statement at the top).
-We’ll cover trait objects in Chapter 17. For now, just
+We’ll cover trait objects in Chapter 18. For now, just
know that Box<dyn Error> means the function will return a type that
implements the Error trait, but we don’t have to specify what particular type
the return value will be. This gives us flexibility to return error values that
@@ -15688,7 +15692,7 @@ standard library. The definition of the trait looks like this:
}
Notice this definition uses some new syntax: type Item and Self::Item,
which are defining an associated type with this trait. We’ll talk about
-associated types in depth in Chapter 19. For now, all you need to know is that
+associated types in depth in Chapter 20. For now, all you need to know is that
this code says implementing the Iterator trait requires that you also define
an Item type, and this Item type is used in the return type of the next
method. In other words, the Item type will be the type returned from the
@@ -16753,7 +16757,7 @@ 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
+Chapter 20), there should be a section explaining why the function is unsafe
and covering the invariants that the function expects callers to uphold.
Most documentation comments don’t need all of these sections, but this is a
@@ -17581,10 +17585,10 @@ time because the data is copied around on the stack. To improve performance in
this situation, we can store the large amount of data on the heap in a box.
Then, only the small amount of pointer data is copied around on the stack,
while the data it references stays in one place on the heap. The third case is
-known as a trait object, and Chapter 17 devotes an entire section, “Using
+known as a trait object, and Chapter 18 devotes an entire section, “Using
Trait Objects That Allow for Values of Different Types,” just to that topic. So what you learn here you’ll apply again in
-Chapter 17!
Before we discuss the heap storage use case for Box<T>, we’ll cover the
syntax and how to interact with values stored within a Box<T>.
@@ -17796,7 +17800,7 @@ 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, too.
+in Chapter 18, too.
The Box<T> type is a smart pointer because it implements the Deref trait,
which allows Box<T> values to be treated like references. When a Box<T>
value goes out of scope, the heap data that the box is pointing to is cleaned
@@ -17984,7 +17988,7 @@ impl<T> Deref for MyBox<T> {
The type Target = T; syntax defines an associated type for the Deref
trait to use. Associated types are a slightly different way of declaring a
generic parameter, but you don’t need to worry about them for now; we’ll cover
-them in more detail in Chapter 19.
+for us; we will discuss unsafe code more in Chapter 20.
We can use types that use the interior mutability pattern only when we can
ensure that the borrowing rules will be followed at runtime, even though the
compiler can’t guarantee that. The unsafe code involved is then wrapped in a
@@ -19981,7 +19985,7 @@ receiver. The abbreviations tx and rx are traditionall
for transmitter and receiver respectively, so we name our variables as such
to indicate each end. We’re using a let statement with a pattern that
destructures the tuples; we’ll discuss the use of patterns in let statements
-and destructuring in Chapter 18. For now, know that using a let statement
+and destructuring in Chapter 19. For now, know that using a let statement
this way is a convenient approach to extract the pieces of the tuple returned
by mpsc::channel.
Let’s move the transmitting end into a spawned thread and have it send one
@@ -20571,7 +20575,7 @@ this in Listing 16-14, we got the error the trait Send is not implemented
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
-we’ll discuss in Chapter 19.
The Sync marker trait indicates that it is safe for the type implementing
Sync to be referenced from multiple threads. In other words, any type T is
@@ -20591,14 +20595,14 @@ also Send and Sync, we don’t have to implement those
marker traits, they don’t even have any methods to implement. They’re just
useful for enforcing invariants related to concurrency.
Manually implementing these traits involves implementing unsafe Rust code.
-We’ll talk about using unsafe Rust code in Chapter 19; for now, the important
+We’ll talk about using unsafe Rust code in Chapter 20; for now, the important
information is that building new concurrent types not made up of Send and
Sync parts requires careful thought to uphold the safety guarantees. “The
Rustonomicon” has more information about these guarantees and how to
uphold them.
This isn’t the last you’ll see of concurrency in this book: the whole next
-chapter focuses on async programming, and the project in Chapter 20 will use the
+chapter focuses on async programming, and the project in Chapter 21 will use the
concepts in this chapter in a more realistic situation than the smaller examples
discussed here.
As mentioned earlier, because very little of how Rust handles concurrency is
@@ -24103,21 +24107,23 @@ can define a struct AveragedCollection that has a field containing
of i32 values. The struct can also have a field that contains the average of
the values in the vector, meaning the average doesn’t have to be computed
on demand whenever anyone needs it. In other words, AveragedCollection will
-cache the calculated average for us. Listing 17-1 has the definition of the
+cache the calculated average for us. Listing 18-1 has the definition of the
AveragedCollection struct:
Listing 17-1: An AveragedCollection struct that
-maintains a list of integers and the average of the items in the
-collection
+Listing 18-1: Listing 18-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
ensure that whenever a value is added or removed from the list, the average is
also updated. We do this by implementing add, remove, and average methods
-on the struct, as shown in Listing 17-2:
+on the struct, as shown in Listing 18-2:
+
+Filename: src/lib.rs
Filename: src/lib.rs
pub struct AveragedCollection {
list: Vec<i32>,
@@ -24150,8 +24156,8 @@ on the struct, as shown in Listing 17-2:
self.average = total as f64 / self.list.len() as f64;
}
}
-
Listing 17-2: Implementations of the public methods
-add, remove, and average on AveragedCollection
+Listing 18-2: Listing 18-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 list using the add method or removed using the remove method, the
@@ -24262,7 +24268,7 @@ implementing our specified trait and a table used to look up trait methods on
that type at runtime. We create a trait object by specifying some sort of
pointer, such as a & reference or a Box<T> smart pointer, then the dyn
keyword, and then specifying the relevant trait. (We’ll talk about the reason
-trait objects must use a pointer in Chapter 19 in the section “Dynamically
+trait objects must use a pointer in Chapter 20 in the section “Dynamically
Sized Types and the Sized Trait.”) We can
use trait objects in place of a generic or concrete type. Wherever we use a
trait object, Rust’s type system will ensure at compile time that any value
@@ -24278,19 +24284,22 @@ But trait objects differ from traditional objects in that we can’t add data to
a trait object. Trait objects aren’t as generally useful as objects in other
languages: their specific purpose is to allow abstraction across common
behavior.
-
Listing 17-3 shows how to define a trait named Draw with one method named
+
Listing 18-3 shows how to define a trait named Draw with one method named
draw:
-
Filename: src/lib.rs
+
+Filename: src/lib.rs
pub trait Draw {
fn draw(&self);
}
-
Listing 17-3: Definition of the Draw trait
+Listing 18-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
+in Chapter 10. Next comes some new syntax: Listing 18-4 defines a struct named
Screen that holds a vector named components. This vector is of type
Box<dyn Draw>, which is a trait object; it’s a stand-in for any type inside
a Box that implements the Draw trait.
-
Filename: src/lib.rs
+
+Filename: src/lib.rs
pub trait Draw {
fn draw(&self);
}
@@ -24298,12 +24307,12 @@ a Box that implements the Draw trait.
pub struct Screen {
pub components: Vec<Box<dyn Draw>>,
}
-
Listing 17-4: Definition of the Screen struct with a
-components field holding a vector of trait objects that implement the Draw
-trait
+Listing 18-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:
-
Filename: src/lib.rs
+draw method on each of its components, as shown in Listing 18-5:
+
+Filename: src/lib.rs
Listing 17-5: A run method on Screen that calls the
-draw method on each component
+Listing 18-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
with one concrete type at a time, whereas trait objects allow for multiple
concrete types to fill in for the trait object at runtime. For example, we
could have defined the Screen struct using a generic type and a trait bound
-as in Listing 17-6:
Listing 17-6: An alternate implementation of the Screen
-struct and its run method using generics and trait bounds
+Listing 18-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
collections, using generics and trait bounds is preferable because the
@@ -24361,8 +24371,9 @@ runtime performance implications.
Button type. Again, actually implementing a GUI library is beyond the scope
of this book, so the draw method won’t have any useful implementation in its
body. To imagine what the implementation might look like, a Button struct
-might have fields for width, height, and label, as shown in Listing 17-7:
-
Filename: src/lib.rs
+might have fields for width, height, and label, as shown in Listing 18-7:
+
+Filename: src/lib.rs
pub trait Draw {
fn draw(&self);
}
@@ -24390,8 +24401,8 @@ impl Draw for Button {
// code to actually draw a button
}
}
-
Listing 17-7: A Button struct that implements the
-Draw trait
+Listing 18-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
same fields plus a placeholder field. Each of the types we want to draw on
@@ -24403,8 +24414,9 @@ happens when a user clicks the button. These kinds of methods won’t apply to
types like TextField.
If someone using our library decides to implement a SelectBox struct that has
width, height, and options fields, they implement the Draw trait on the
-SelectBox type as well, as shown in Listing 17-8:
-
Filename: src/main.rs
+SelectBox type as well, as shown in Listing 18-8:
+
+Filename: src/main.rs
use gui::Draw;
struct SelectBox {
@@ -24420,14 +24432,15 @@ impl Draw for SelectBox {
}
fn main() {}
-
Listing 17-8: Another crate using gui and implementing
-the Draw trait on a SelectBox struct
+Listing 18-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
by putting each in a Box<T> to become a trait object. They can then call the
run method on the Screen instance, which will call draw on each of the
-components. Listing 17-9 shows this implementation:
-
Filename: src/main.rs
+components. Listing 18-9 shows this implementation:
+
+Filename: src/main.rs
Listing 17-9: Using trait objects to store values of
-different types that implement the same trait
+Listing 18-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
new type and draw it because SelectBox implements the Draw trait, which
@@ -24476,7 +24489,7 @@ means it implements the draw method.
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
+in Listing 18-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<dyn Draw> as the type of the values in the components
@@ -24487,9 +24500,10 @@ similar to code using duck typing is that we never have to check whether a
value implements a particular method at runtime or worry about getting errors
if a value doesn’t implement a method but we call it anyway. Rust won’t compile
our code if the values don’t implement the traits that the trait objects need.
-
For example, Listing 17-10 shows what happens if we try to create a Screen
+
For example, Listing 18-10 shows what happens if we try to create a Screen
with a String as a component:
Listing 17-10: Attempting to use a type that doesn’t
-implement the trait object’s trait
+Listing 18-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:
$ cargo run
Compiling gui v0.1.0 (file:///projects/gui)
@@ -24538,8 +24552,8 @@ runtime, Rust uses the pointers inside the trait object to know which method to
call. This lookup incurs a runtime cost that doesn’t occur with static
dispatch. Dynamic dispatch also prevents the compiler from choosing to inline a
method’s code, which in turn prevents some optimizations. However, we did get
-extra flexibility in the code that we wrote in Listing 17-5 and were able to
-support in Listing 17-9, so it’s a trade-off to consider.
+extra flexibility in the code that we wrote in Listing 18-5 and were able to
+support in Listing 18-9, so it’s a trade-off to consider.
The state pattern is an object-oriented design pattern. The crux of the
pattern is that we define a set of states a value can have internally. The
@@ -24572,10 +24586,11 @@ accidentally be published.
Any other changes attempted on a post should have no effect. For example, if we
try to approve a draft blog post before we’ve requested a review, the post
should remain an unpublished draft.
-
Listing 17-11 shows this workflow in code form: this is an example usage of the
+
Listing 18-11 shows this workflow in code form: this is an example usage of the
API we’ll implement in a library crate named blog. This won’t compile yet
because we haven’t implemented the blog crate.
-
Filename: src/main.rs
+
+Filename: src/main.rs
use blog::Post;
fn main() {
@@ -24590,8 +24605,8 @@ fn main() {
post.approve();
assert_eq!("I ate a salad for lunch today", post.content());
}
-
Listing 17-11: Code that demonstrates the desired
-behavior we want our blog crate to have
+Listing 18-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. We
want to allow text to be added to the blog post. If we try to get the post’s
content immediately, before approval, we shouldn’t get any text because the
@@ -24615,13 +24630,14 @@ make a mistake with the states, like publishing a post before it’s reviewed.
Let’s get started on the implementation of the library! We know we need a
public Post struct that holds some content, so we’ll start with the
definition of the struct and an associated public new function to create an
-instance of Post, as shown in Listing 17-12. We’ll also make a private
+instance of Post, as shown in Listing 18-12. We’ll also make a private
State trait that will define the behavior that all state objects for a Post
must have.
Then Post will hold a trait object of Box<dyn State> inside an Option<T>
in a private field named state to hold the state object. You’ll see why the
Option<T> is necessary in a bit.
-
Filename: src/lib.rs
+
+Filename: src/lib.rs
pub struct Post {
state: Option<Box<dyn State>>,
content: String,
@@ -24641,9 +24657,8 @@ trait State {}
struct Draft {}
impl State for Draft {}
-
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
+Listing 18-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
implement the State trait. For now, the trait doesn’t have any methods, and
@@ -24656,13 +24671,13 @@ a draft. Because the state field of Post is private, t
create a Post in any other state! In the Post::new function, we set the
content field to a new, empty String.
We saw in Listing 17-11 that we want to be able to call a method named
+
We saw in Listing 18-11 that we want to be able to call a method named
add_text and pass it a &str that is then added as the text content of the
blog post. We implement this as a method, rather than exposing the content
field as pub, so that later we can implement a method that will control how
the content field’s data is read. The add_text method is pretty
-straightforward, so let’s add the implementation in Listing 17-13 to the impl Post block:
-
Filename: src/lib.rs
+straightforward, so let’s add the implementation in Listing 18-13 to the impl Post block:
+
pub struct Post {
state: Option<Box<dyn State>>,
content: String,
@@ -24687,8 +24702,8 @@ straightforward, so let’s add the implementation in Listing 17-13 to the struct Draft {}
impl State for Draft {}
-
Listing 17-13: Implementing the add_text method to add
-text to a post’s content
+Listing 18-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
push_str on the String in content and pass the text argument to add to
@@ -24699,13 +24714,14 @@ support.
Even after we’ve called add_text and added some content to our post, we still
want the content method to return an empty string slice because the post is
-still in the draft state, as shown on line 7 of Listing 17-11. For now, let’s
+still in the draft state, as shown on line 7 of Listing 18-11. For now, let’s
implement the content method with the simplest thing that will fulfill this
requirement: always returning an empty string slice. We’ll change this later
once we implement the ability to change a post’s state so it can be published.
So far, posts can only be in the draft state, so the post content should always
-be empty. Listing 17-14 shows this placeholder implementation:
pub struct Post {
state: Option<Box<dyn State>>,
content: String,
@@ -24734,14 +24750,15 @@ be empty. Listing 17-14 shows this placeholder implementation:
struct Draft {}
impl State for Draft {}
-
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 line 7
+Listing 18-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 18-11 up to line 7
works as intended.
Next, we need to add functionality to request a review of a post, which should
-change its state from Draft to PendingReview. Listing 17-15 shows this code:
-
Filename: src/lib.rs
+change its state from Draft to PendingReview. Listing 18-15 shows this code:
+
+Filename: src/lib.rs
pub struct Post {
state: Option<Box<dyn State>>,
content: String,
@@ -24790,8 +24807,8 @@ impl State for PendingReview {
self
}
}
-
Listing 17-15: Implementing request_review methods on
-Post and the State trait
+Listing 18-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. Then we call an internal request_review method on the
current state of Post, and this second request_review method consumes the
@@ -24826,14 +24843,15 @@ state is responsible for its own rules.
We’ll leave the content method on Post as is, returning an empty string
slice. We can now have a Post in the PendingReview state as well as in the
Draft state, but we want the same behavior in the PendingReview state.
-Listing 17-11 now works up to line 10!
The approve method will be similar to the request_review method: it will
set state to the value that the current state says it should have when that
-state is approved, as shown in Listing 17-16:
-
Filename: src/lib.rs
+state is approved, as shown in Listing 18-16:
+
+Filename: src/lib.rs
pub struct Post {
state: Option<Box<dyn State>>,
content: String,
@@ -24911,8 +24929,8 @@ impl State for Published {
self
}
}
-
Listing 17-16: Implementing the approve method on
-Post and the State trait
+Listing 18-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.
Similar to the way request_review on PendingReview works, if we call the
@@ -24925,8 +24943,9 @@ state in those cases.
Now we need to update the content method on Post. We want the value
returned from content to depend on the current state of the Post, so we’re
going to have the Post delegate to a content method defined on its state,
-as shown in Listing 17-17:
-
Filename: src/lib.rs
+as shown in Listing 18-17:
+
+Filename: src/lib.rs
pub struct Post {
state: Option<Box<dyn State>>,
content: String,
@@ -25003,8 +25022,8 @@ as shown in Listing 17-17:
self
}
}
-
Listing 17-17: Updating the content method on Post to
-delegate to a content method on State
+Listing 18-17: Updating the content method on Post to delegate to a content method on State
+
Because the goal is to keep all these rules inside the structs that implement
State, we call a content method on the value in state and pass the post
instance (that is, self) as an argument. Then we return the value that’s
@@ -25025,8 +25044,9 @@ will take effect on the & and the Box so the State trait. That means
we need to add content to the State trait definition, and that is where
we’ll put the logic for what content to return depending on which state we
-have, as shown in Listing 17-18:
-
Filename: src/lib.rs
+have, as shown in Listing 18-18:
+
+Filename: src/lib.rs
pub struct Post {
state: Option<Box<dyn State>>,
content: String,
@@ -25113,8 +25133,8 @@ impl State for Published {
&post.content
}
}
-
Listing 17-18: Adding the content method to the State
-trait
+Listing 18-18: Adding the content method to the State trait
+
We add a default implementation for the content method that returns an empty
string slice. That means we don’t need to implement content on the Draft
and PendingReview structs. The Published struct will override the content
@@ -25123,7 +25143,7 @@ method and return the value in post.content.
Chapter 10. We’re taking a reference to a post as an argument and returning a
reference to part of that post, so the lifetime of the returned reference is
related to the lifetime of the post argument.
-
And we’re done—all of Listing 17-11 now works! We’ve implemented the state
+
And we’re done—all of Listing 18-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.
@@ -25181,7 +25201,7 @@ and approve methods on Post. Both methods delegate to
the same method on the value in the state field of Option and set the new
value of the state field to the result. If we had a lot of methods on Post
that followed this pattern, we might consider defining a macro to eliminate the
-repetition (see the “Macros” section in Chapter 19).
+repetition (see the “Macros” section in Chapter 20).
By implementing the state pattern exactly as it’s defined for object-oriented
languages, we’re not taking as full advantage of Rust’s strengths as we could.
Let’s look at some changes we can make to the blog crate that can make
@@ -25192,8 +25212,9 @@ trade-offs. Rather than encapsulating the states and transitions completely so
outside code has no knowledge of them, we’ll encode the states into different
types. Consequently, Rust’s type checking system will prevent attempts to use
draft posts where only published posts are allowed by issuing a compiler error.
-
Let’s consider the first part of main in Listing 17-11:
-
Filename: src/main.rs
+
Let’s consider the first part of main in Listing 18-11:
+
+Filename: src/main.rs
use blog::Post;
fn main() {
@@ -25208,6 +25229,7 @@ draft posts where only published posts are allowed by issuing a compiler error.<
post.approve();
assert_eq!("I ate a salad for lunch today", post.content());
}
+
We still enable the creation of new posts in the draft state using Post::new
and the ability to add text to the post’s content. But instead of having a
content method on a draft post that returns an empty string, we’ll make it so
@@ -25215,9 +25237,10 @@ draft posts don’t have the content method at all. That way, if we
a draft post’s content, we’ll get a compiler error telling us the method
doesn’t exist. As a result, it will be impossible for us to accidentally
display draft post content in production, because that code won’t even compile.
-Listing 17-19 shows the definition of a Post struct and a DraftPost struct,
+Listing 18-19 shows the definition of a Post struct and a DraftPost struct,
as well as methods on each:
Listing 17-19: A Post with a content method and a
-DraftPost without a content method
+Listing 18-19: A Post with a content method and 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
we’re moving the encoding of the state to the types of the structs. The Post
@@ -25266,8 +25289,9 @@ pending review state should still not display any content. Let’s implement
these constraints by adding another struct, PendingReviewPost, defining the
request_review method on DraftPost to return a PendingReviewPost, and
defining an approve method on PendingReviewPost to return a Post, as
-shown in Listing 17-20:
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
+Listing 18-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
them into a PendingReviewPost and a published Post, respectively. This way,
@@ -25332,8 +25355,9 @@ called on, so we need to add more let post = shadowing assignments
the returned instances. We also can’t have the assertions about the draft and
pending review posts’ contents be empty strings, nor do we need them: we can’t
compile code that tries to use the content of posts in those states any longer.
-The updated code in main is shown in Listing 17-21:
-
Filename: src/main.rs
+The updated code in main is shown in Listing 18-21:
+
+Filename: src/main.rs
use blog::Post;
fn main() {
@@ -25347,8 +25371,8 @@ fn main() {
assert_eq!("I ate a salad for lunch today", post.content());
}
-
Listing 17-21: Modifications to main to use the new
-implementation of the blog post workflow
+Listing 18-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:
the transformations between the states are no longer encapsulated entirely
@@ -25357,7 +25381,7 @@ now impossible because of the type system and the type checking that happens at
compile time! This ensures that certain bugs, such as display of the content of
an unpublished post, will be discovered before they make it to production.
Try the tasks suggested at the start of this section on the blog crate as it
-is after Listing 17-21 to see what you think about the design of this version
+is after Listing 18-21 to see what you think about the design of this version
of the code. Note that some of the tasks might be completed already in this
design.
We’ve seen that even though Rust is capable of implementing object-oriented
@@ -25446,14 +25470,15 @@ chapter.
way to write the equivalent of a match that only matches one case.
Optionally, if let can have a corresponding else containing code to run if
the pattern in the if let doesn’t match.
-
Listing 18-1 shows that it’s also possible to mix and match if let, else if, and else if let expressions. Doing so gives us more flexibility than a
+
Listing 19-1 shows that it’s also possible to mix and match if let, else if, and else if let expressions. Doing so gives us more flexibility than a
match expression in which we can express only one value to compare with the
patterns. Also, Rust doesn’t require that the conditions in a series of if let, else if, else if let arms relate to each other.
-
The code in Listing 18-1 determines what color to make your background based on
+
The code in Listing 19-1 determines what color to make your background based on
a series of checks for several conditions. For this example, we’ve created
variables with hardcoded values that a real program might receive from user
input.
-
Filename: src/main.rs
+
+Filename: src/main.rs
fn main() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
@@ -25473,8 +25498,8 @@ input.
println!("Using blue as the background color");
}
}
-
Listing 18-1: Mixing if let, else if, else if let,
-and else
+Listing 19-1: Mixing if let, else if, else if let, and else
+
If the user specifies a favorite color, that color is used as the background.
If no favorite color is specified and today is Tuesday, the background color is
green. Otherwise, if the user specifies their age as a string and we can parse
@@ -25497,8 +25522,9 @@ not alert us to the possible logic bug.
Similar in construction to if let, the while let conditional loop allows a
while loop to run for as long as a pattern continues to match. In Listing
-18-2 we code a while let loop that uses a vector as a stack and prints the
+19-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.
+
fn main() {
let mut stack = Vec::new();
@@ -25510,8 +25536,8 @@ values in the vector in the opposite order in which they were pushed.
println!("{top}");
}
}
-
Listing 18-2: Using a while let loop to print values
-for as long as stack.pop() returns Some
+Listing 19-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, pop
returns None. The while loop continues running the code in its block as
@@ -25519,9 +25545,10 @@ long as pop returns Some. When pop return
use while let to pop every element off our stack.
In a for loop, the value that directly follows the keyword for is a
-pattern. For example, in for x in y the x is the pattern. Listing 18-3
+pattern. For example, in for x in y the x is the pattern. Listing 19-3
demonstrates how to use a pattern in a for loop to destructure, or break
apart, a tuple as part of the for loop.
+
fn main() {
let v = vec!['a', 'b', 'c'];
@@ -25529,9 +25556,9 @@ apart, a tuple as part of the for loop.
println!("{value} is at index {index}");
}
}
-
Listing 18-3: Using a pattern in a for loop to
-destructure a tuple
-
The code in Listing 18-3 will print the following:
+Listing 19-3: Using a pattern in a for loop to destructure a tuple
+
+
The code in Listing 19-3 will print the following:
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.52s
@@ -25566,25 +25593,27 @@ the expression against the pattern and assigns any names it finds. So in the
the variable x.” Because the name x is the whole pattern, this pattern
effectively means “bind everything to the variable x, whatever the value is.”
To see the pattern matching aspect of let more clearly, consider Listing
-18-4, which uses a pattern with let to destructure a tuple.
+19-4, which uses a pattern with let to destructure a tuple.
+
fn main() {
let (x, y, z) = (1, 2, 3);
}
-
Listing 18-4: Using a pattern to destructure a tuple and
-create three variables at once
+Listing 19-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, so Rust
binds 1 to x, 2 to y, and 3 to z. You can think of this tuple
pattern as nesting three individual variable patterns inside it.
If the number of elements in the pattern doesn’t match the number of elements
in the tuple, the overall type won’t match and we’ll get a compiler error. For
-example, Listing 18-5 shows an attempt to destructure a tuple with three
+example, Listing 19-5 shows an attempt to destructure a tuple with three
elements into two variables, which won’t work.
+
fn main() {
let (x, y) = (1, 2, 3);
}
-
Listing 18-5: Incorrectly constructing a pattern whose
-variables don’t match the number of elements in the tuple
+Listing 19-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:
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
@@ -25609,20 +25638,22 @@ is that we have too many variables in the pattern, the solution is to make the
types match by removing variables so the number of variables equals the number
of elements in the tuple.
Function parameters can also be patterns. The code in Listing 18-6, which
+
Function parameters can also be patterns. The code in Listing 19-6, which
declares a function named foo that takes one parameter named x of type
i32, should by now look familiar.
+
fn foo(x: i32) {
// code goes here
}
fn main() {}
-
Listing 18-6: A function signature uses patterns in the
-parameters
+Listing 19-6: A function signature uses 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
+function’s arguments to the pattern. Listing 19-7 splits the values in a tuple
as we pass it to a function.
Listing 18-7: A function with parameters that destructure
-a tuple
+Listing 19-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.
We can also use patterns in closure parameter lists in the same way as in
@@ -25662,15 +25693,16 @@ of refutability so you can respond when you see it in an error message. In
those cases, you’ll need to change either the pattern or the construct you’re
using the pattern with, depending on the intended behavior of the code.
Let’s look at an example of what happens when we try to use a refutable pattern
-where Rust requires an irrefutable pattern and vice versa. Listing 18-8 shows a
+where Rust requires an irrefutable pattern and vice versa. Listing 19-8 shows a
let statement, but for the pattern we’ve specified Some(x), a refutable
pattern. As you might expect, this code will not compile.
+
fn main() {
let some_option_value: Option<i32> = None;
let Some(x) = some_option_value;
}
-
Listing 18-8: Attempting to use a refutable pattern with
-let
+Listing 19-8: Attempting to use a refutable pattern with let
+
If some_option_value was a None value, it would fail to match the pattern
Some(x), meaning the pattern is refutable. However, the let statement can
only accept an irrefutable pattern because there is nothing valid the code can
@@ -25685,7 +25717,7 @@ error[E0005]: refutable pattern in local binding
| ^^^^^^^ 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: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `Option<i32>`
help: you might want to use `let else` to handle the variant that isn't matched
|
@@ -25701,26 +25733,28 @@ pattern Some(x), Rust rightfully produces a compiler error.
fix it by changing the code that uses the pattern: instead of using let, we
can use if let. Then if the pattern doesn’t match, the code will just skip
the code in the curly brackets, giving it a way to continue validly. Listing
-18-9 shows how to fix the code in Listing 18-8.
+19-9 shows how to fix the code in Listing 19-8.
+
fn main() {
let some_option_value: Option<i32> = None;
if let Some(x) = some_option_value {
println!("{x}");
}
}
-
Listing 18-9: Using if let and a block with refutable
-patterns instead of let
+Listing 19-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 now. However,
if we give if let an irrefutable pattern (a pattern that will always
-match), such as x, as shown in Listing 18-10, the compiler will give a
+match), such as x, as shown in Listing 19-10, the compiler will give a
warning.
+
fn main() {
if let x = 5 {
println!("{x}");
};
}
-
Listing 18-10: Attempting to use an irrefutable pattern
-with if let
+Listing 19-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:
$ cargo run
@@ -25773,12 +25807,13 @@ them many times in the book. However, there is a complication when you use
named variables in match expressions. Because match starts a new scope,
variables declared as part of a pattern inside the match expression will
shadow those with the same name outside the match construct, as is the case
-with all variables. In Listing 18-11, we declare a variable named x with the
+with all variables. In Listing 19-11, we declare a variable named x with the
value Some(5) and a variable y with the value 10. We then create a
match expression on the value x. Look at the patterns in the match arms and
println! at the end, and try to figure out what the code will print before
running this code or reading further.
-
Filename: src/main.rs
+
+Filename: src/main.rs
fn main() {
let x = Some(5);
let y = 10;
@@ -25791,8 +25826,8 @@ running this code or reading further.
println!("at the end: x = {x:?}, y = {y}");
}
-
Listing 18-11: A match expression with an arm that
-introduces a shadowed variable y
+Listing 19-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 doesn’t match the defined value of x, so the code
continues.
@@ -25866,9 +25901,10 @@ numeric values, ranges are only allowed with numeric or char values
We can also use patterns to destructure structs, enums, and tuples to use
different parts of these values. Let’s walk through each value.
Listing 18-12: Destructuring a struct’s fields into
-separate variables
+Listing 19-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
variables in the pattern don’t have to match the field names of the struct.
@@ -25891,10 +25927,11 @@ easier to remember which variables came from which fields. Because of this
common usage, and because writing let Point { x: x, y: y } = p; contains a
lot of duplication, Rust has a shorthand for patterns that match struct fields:
you only need to list the name of the struct field, and the variables created
-from the pattern will have the same names. Listing 18-13 behaves in the same
-way as the code in Listing 18-12, but the variables created in the let
+from the pattern will have the same names. Listing 19-13 behaves in the same
+way as the code in Listing 19-12, but the variables created in the let
pattern are x and y instead of a and b.
Listing 18-13: Destructuring struct fields using struct
-field shorthand
+Listing 19-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
values from the p struct.
@@ -25916,10 +25953,11 @@ values from the p struct.
rather than creating variables for all the fields. Doing so allows us to test
some of the fields for particular values while creating variables to
destructure the other fields.
-
In Listing 18-14, we have a match expression that separates Point values
+
In Listing 19-14, we have a match expression that separates Point values
into three cases: points that lie directly on the x axis (which is true when
y = 0), on the y axis (x = 0), or neither.
-
Filename: src/main.rs
+
+Filename: src/main.rs
struct Point {
x: i32,
y: i32,
@@ -25936,8 +25974,8 @@ into three cases: points that lie directly on the x axis (which is
}
}
}
-
Listing 18-14: Destructuring and matching literal values
-in one pattern
+Listing 19-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
creates an x variable that we can use in the code for this arm.
@@ -25954,9 +25992,10 @@ and the y axis, this code would only print On the x axis at 0
We’ve destructured enums in this book (for example, Listing 6-5 in Chapter 6),
but haven’t yet explicitly discussed that the pattern to destructure an enum
corresponds to the way the data stored within the enum is defined. As an
-example, in Listing 18-15 we use the Message enum from Listing 6-2 and write
+example, in Listing 19-15 we use the Message enum from Listing 6-2 and write
a match with patterns that will destructure each inner value.
Listing 18-15: Destructuring enum variants that hold
-different kinds of values
+Listing 19-15: Destructuring enum variants that hold different kinds of values
+
This code will print Change the color to red 0, green 160, and blue 255. Try
changing the value of msg to see the code from the other arms run.
For enum variants without any data, like Message::Quit, we can’t destructure
@@ -25993,7 +26032,7 @@ and no variables are in that pattern.
similar to the pattern we specify to match structs. After the variant name, we
place curly brackets and then list the fields with variables so we break apart
the pieces to use in the code for this arm. Here we use the shorthand form as
-we did in Listing 18-13.
+we did in Listing 19-13.
For tuple-like enum variants, like Message::Write that holds a tuple with one
element and Message::ChangeColor that holds a tuple with three elements, the
pattern is similar to the pattern we specify to match tuples. The number of
@@ -26002,8 +26041,9 @@ matching.
So far, our examples have all been matching structs or enums one level deep,
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.
+code in Listing 19-15 to support RGB and HSV colors in the ChangeColor
+message, as shown in Listing 19-16.
+
The pattern of the first arm in the match expression matches a
Message::ChangeColor enum variant that contains a Color::Rgb variant; then
the pattern binds to the three inner i32 values. The pattern of the second
@@ -26064,8 +26105,9 @@ parts of a value. Let’s explore how and why to use each of these patterns.
We’ve used the underscore as a wildcard pattern that will match any value but
not bind to the value. This is especially useful as the last arm in a match
expression, but we can also use it in any pattern, including function
-parameters, as shown in Listing 18-17.
-
Filename: src/main.rs
+parameters, as shown in Listing 19-17.
+
+Filename: src/main.rs
fn foo(_: i32, y: i32) {
println!("This code only uses the y parameter: {y}");
}
@@ -26073,7 +26115,8 @@ parameters, as shown in Listing 18-17.
fn main() {
foo(3, 4);
}
-
Listing 18-17: Using _ in a function signature
+Listing 19-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.
In most cases when you no longer need a particular function parameter, you
@@ -26086,10 +26129,11 @@ would if you used a name instead.
We can also use _ inside another pattern to ignore just part of a value, for
example, when we want to test for only part of a value but have no use for the
-other parts in the corresponding code we want to run. Listing 18-18 shows code
+other parts in the corresponding code we want to run. Listing 19-18 shows code
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.
+
fn main() {
let mut setting_value = Some(5);
let new_setting_value = Some(10);
@@ -26105,9 +26149,8 @@ setting but can unset the setting and give it a value if it is currently unset.<
println!("setting is {setting_value:?}");
}
-
Listing 18-18: Using an underscore within patterns that
-match Some variants when we don’t need to use the value inside the
-Some
+Listing 19-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
the values inside either Some variant, but we do need to test for the case
@@ -26118,8 +26161,9 @@ changed.
None) expressed by the _ pattern in the second arm, we want to allow
new_setting_value to become setting_value.
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
+particular values. Listing 19-19 shows an example of ignoring the second and
fourth values in a tuple of five items.
+
fn main() {
let numbers = (2, 4, 8, 16, 32);
@@ -26129,7 +26173,8 @@ fourth values in a tuple of five items.
}
}
}
-
Listing 18-19: Ignoring multiple parts of a tuple
+Listing 19-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.
@@ -26138,21 +26183,23 @@ warning because an unused variable could be a bug. However, sometimes it’s
useful to be able to create a variable you won’t use yet, such as when you’re
prototyping or just starting a project. In this situation, you can tell Rust
not to warn you about the unused variable by starting the name of the variable
-with an underscore. In Listing 18-20, we create two unused variables, but when
+with an underscore. In Listing 19-20, we create two unused variables, but when
we compile this code, we should only get a warning about one of them.
-
Filename: src/main.rs
+
+Filename: src/main.rs
fn main() {
let _x = 5;
let y = 10;
}
-
Listing 18-20: Starting a variable name with an
-underscore to avoid getting unused variable warnings
+Listing 19-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.
Note that there is a subtle difference between using only _ and using a name
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.
+distinction matters, Listing 19-21 will provide us with an error.
+
fn main() {
let s = Some(String::from("Hello!"));
@@ -26162,12 +26209,13 @@ distinction matters, Listing 18-21 will provide us with an error.
println!("{s:?}");
}
-
Listing 18-21: An unused variable starting with an
-underscore still binds the value, which might take ownership of the value
+Listing 19-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
+doesn’t ever bind to the value. Listing 19-22 will compile without any errors
because s doesn’t get moved into _.
+
fn main() {
let s = Some(String::from("Hello!"));
@@ -26177,17 +26225,18 @@ because s doesn’t get moved into _.
println!("{s:?}");
}
-
Listing 18-22: Using an underscore does not bind the
-value
+Listing 19-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.
With values that have many parts, we can use the .. syntax to use specific
parts and ignore the rest, avoiding the need to list underscores for each
ignored value. The .. pattern ignores any parts of a value that we haven’t
-explicitly matched in the rest of the pattern. In Listing 18-23, we have a
+explicitly matched in the rest of the pattern. In Listing 19-23, we have a
Point struct that holds a coordinate in three-dimensional space. In the
match expression, we want to operate only on the x coordinate and ignore
the values in the y and z fields.
+
fn main() {
struct Point {
x: i32,
@@ -26201,15 +26250,16 @@ the values in the y and z fields.
Point { x, .. } => println!("x is {x}"),
}
}
-
Listing 18-23: Ignoring all fields of a Point except
-for x by using ..
+Listing 19-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
structs that have lots of fields in situations where only one or two fields are
relevant.
-
The syntax .. will expand to as many values as it needs to be. Listing 18-24
+
The syntax .. will expand to as many values as it needs to be. Listing 19-24
shows how to use .. with a tuple.
-
Filename: src/main.rs
+
+Filename: src/main.rs
fn main() {
let numbers = (2, 4, 8, 16, 32);
@@ -26219,15 +26269,16 @@ shows how to use .. with a tuple.
}
}
}
-
Listing 18-24: Matching only the first and last values in
-a tuple and ignoring all other values
+Listing 19-24: Matching only the first and last values in a tuple and ignoring all other values
+
In this code, the first and last value are matched with first and last. The
.. will match and ignore everything in the middle.
However, using .. must be unambiguous. If it is unclear which values are
intended for matching and which should be ignored, Rust will give us an error.
-Listing 18-25 shows an example of using .. ambiguously, so it will not
+Listing 19-25 shows an example of using .. ambiguously, so it will not
compile.
Listing 18-25: An attempt to use .. in an ambiguous
-way
+Listing 19-25: An attempt to use .. in an ambiguous way
+
When we compile this example, we get this error:
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
@@ -26263,9 +26314,10 @@ compiler error because using .. in two places like this is ambiguou
A match guard is an additional if condition, specified after the pattern in
a match arm, that must also match for that arm to be chosen. Match guards are
useful for expressing more complex ideas than a pattern alone allows.
-
The condition can use variables created in the pattern. Listing 18-26 shows a
+
The condition can use variables created in the pattern. Listing 19-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).
+
fn main() {
let num = Some(4);
@@ -26275,7 +26327,8 @@ guard of if x % 2 == 0 (which will be true if the number is even).<
None => (),
}
}
-
Listing 18-26: Adding a match guard to a pattern
+Listing 19-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
the match guard checks whether the remainder of dividing x by 2 is equal to
@@ -26288,13 +26341,14 @@ second arm doesn’t have a match guard and therefore matches any Some
-
In Listing 18-11, we mentioned that we could use match guards to solve our
+
In Listing 19-11, we mentioned that we could use match guards to solve our
pattern-shadowing problem. Recall that we created a new variable inside the
pattern in the match expression instead of using the variable outside the
match. That new variable meant we couldn’t test against the value of the
-outer variable. Listing 18-27 shows how we can use a match guard to fix this
+outer variable. Listing 19-27 shows how we can use a match guard to fix this
problem.
-
Filename: src/main.rs
+
+Filename: src/main.rs
fn main() {
let x = Some(5);
let y = 10;
@@ -26307,8 +26361,8 @@ problem.
println!("at the end: x = {x:?}, y = {y}");
}
-
Listing 18-27: Using a match guard to test for equality
-with an outer variable
+Listing 19-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,
meaning we can use the outer y in the match guard. Instead of specifying the
@@ -26321,10 +26375,11 @@ we can look for a value that has the same value as the outer y by c
n to y.
You can also use the or operator | in a match guard to specify multiple
patterns; the match guard condition will apply to all the patterns. Listing
-18-28 shows the precedence when combining a pattern that uses | with a match
+19-28 shows the precedence when combining a pattern that uses | with a match
guard. The important part of this example is that the if y match guard
applies to 4, 5, and6, even though it might look like if y only
applies to 6.
+
fn main() {
let x = 4;
let y = false;
@@ -26334,8 +26389,8 @@ applies to 6.
_ => println!("no"),
}
}
-
Listing 18-28: Combining multiple patterns with a match
-guard
+Listing 19-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 6and if y is true. When this code runs, the
pattern of the first arm matches because x is 4, but the match guard if y
@@ -26355,11 +26410,12 @@ were applied only to the final value in the list of values specified using the
yes.
The at operator @ lets us create a variable that holds a value at the same
-time as we’re testing that value for a pattern match. In Listing 18-29, we want
+time as we’re testing that value for a pattern match. In Listing 19-29, we want
to test that a Message::Helloid field is within the range 3..=7. We also
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.
+
fn main() {
enum Message {
Hello { id: i32 },
@@ -26377,8 +26433,8 @@ field, but for this example we’ll use a different name.
Message::Hello { id } => println!("Found some other id: {id}"),
}
}
-
Listing 18-29: Using @ to bind to a value in a pattern
-while also testing it
+Listing 19-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
while also testing that the value matched the range pattern.
In the second arm, where we only have a range specified in the pattern, the code
@@ -26404,7 +26460,7 @@ variables. We can create simple or complex patterns to suit our needs.
By now, you’ve learned the most commonly used parts of the Rust programming
-language. Before we do one more project in Chapter 20, we’ll look at a few
+language. Before we do one more project in Chapter 21, we’ll look at a few
aspects of the language you might run into every once in a while, but may not
use every day. You can use this chapter as a reference for when you encounter
any unknowns. The features covered here are useful in very specific situations.
@@ -26499,15 +26555,17 @@ mutable pointers or multiple mutable pointers to the same location
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.
-
Listing 19-1 shows how to create an immutable and a mutable raw pointer from
+
Listing 20-1 shows how to create an immutable and a mutable raw pointer from
references.
+
fn main() {
let mut num = 5;
let r1 = &num as *const i32;
let r2 = &mut num as *mut i32;
}
-
Listing 19-1: Creating raw pointers from references
+Listing 20-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
unsafe block, as you’ll see in a bit.
@@ -26517,21 +26575,23 @@ directly from references guaranteed to be valid, we know these particular raw
pointers are valid, but we can’t make that assumption about just any raw
pointer.
To demonstrate this, next we’ll create a raw pointer whose validity we can’t be
-so certain of. Listing 19-2 shows how to create a raw pointer to an arbitrary
+so certain of. Listing 20-2 shows how to create a raw pointer to an arbitrary
location in memory. Trying to use arbitrary memory is undefined: there might be
data at that address or there might not, the compiler might optimize the code
so there is no memory access, or the program might error with a segmentation
fault. Usually, there is no good reason to write code like this, but it is
possible.
+
fn main() {
let address = 0x012345usize;
let r = address as *const i32;
}
-
Listing 19-2: Creating a raw pointer to an arbitrary
-memory address
+Listing 20-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
+raw pointers and read the data being pointed to. In Listing 20-3, we use the
dereference operator * on a raw pointer that requires an unsafe block.
+
fn main() {
let mut num = 5;
@@ -26543,11 +26603,11 @@ dereference operator * on a raw pointer that requires an unsa
println!("r2 is: {}", *r2);
}
}
-
Listing 19-3: Dereferencing raw pointers within an
-unsafe block
+Listing 20-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.
-
Note also that in Listing 19-1 and 19-3, we created *const i32 and *mut i32
+
Note also that in Listing 20-1 and 20-3, we created *const i32 and *mut i32
raw pointers that both pointed to the same memory location, where num is
stored. If we instead tried to create an immutable and a mutable reference to
num, the code would not have compiled because Rust’s ownership rules don’t
@@ -26608,7 +26668,8 @@ a common abstraction. As an example, let’s study the split_at_mut
from the standard library, which requires some unsafe code. We’ll explore how
we might implement it. This safe method is defined on mutable slices: it takes
one slice and makes it two by splitting the slice at the index given as an
-argument. Listing 19-4 shows how to use split_at_mut.
+argument. Listing 20-4 shows how to use split_at_mut.
+
fn main() {
let mut v = vec![1, 2, 3, 4, 5, 6];
@@ -26619,12 +26680,13 @@ argument. Listing 19-4 shows how to use split_at_mut.
assert_eq!(a, &mut [1, 2, 3]);
assert_eq!(b, &mut [4, 5, 6]);
}
-
Listing 19-4: Using the safe split_at_mut
-function
+Listing 20-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
+something like Listing 20-5, which won’t compile. For simplicity, we’ll
implement split_at_mut as a function rather than a method and only for slices
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();
@@ -26637,8 +26699,8 @@ of i32 values rather than for a generic type T.
let mut vector = vec![1, 2, 3, 4, 5, 6];
let (left, right) = split_at_mut(&mut vector, 3);
}
-
Listing 19-5: An attempted implementation of
-split_at_mut using only safe Rust
+Listing 20-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
less than or equal to the length. The assertion means that if we pass an index
@@ -26647,7 +26709,7 @@ before it attempts to use that index.
Then we return two mutable slices in a tuple: one from the start of the
original slice to the mid index and another from mid to the end of the
slice.
-
When we try to compile the code in Listing 19-5, we’ll get an error.
+
When we try to compile the code in Listing 20-5, we’ll get an error.
$ cargo run
Compiling unsafe-example v0.1.0 (file:///projects/unsafe-example)
error[E0499]: cannot borrow `*values` as mutable more than once at a time
@@ -26673,8 +26735,9 @@ the slice; it only knows that we’re borrowing from the same slice twice.
Borrowing different parts of a slice is fundamentally okay because the two
slices aren’t overlapping, but Rust isn’t smart enough to know this. When we
know code is okay, but Rust doesn’t, it’s time to reach for unsafe code.
-
Listing 19-6 shows how to use an unsafe block, a raw pointer, and some calls
+
Listing 20-6 shows how to use an unsafe block, a raw pointer, and some calls
to unsafe functions to make the implementation of split_at_mut work.
Listing 19-6: Using unsafe code in the implementation of
-the split_at_mut function
+Listing 20-6: Using unsafe code in the implementation of the split_at_mut function
+
Recall from “The Slice Type” section in
Chapter 4 that slices are a pointer to some data and the length of the slice.
We use the len method to get the length of a slice and the as_mut_ptr
@@ -26724,9 +26787,10 @@ appropriate use of unsafe.
abstraction to the unsafe code with an implementation of the function that uses
unsafe code in a safe way, because it creates only valid pointers from the
data this function has access to.
-
In contrast, the use of slice::from_raw_parts_mut in Listing 19-7 would
+
In contrast, the use of slice::from_raw_parts_mut in Listing 20-7 would
likely crash when the slice is used. This code takes an arbitrary memory
location and creates a slice 10,000 items long.
+
fn main() {
use std::slice;
@@ -26735,8 +26799,8 @@ location and creates a slice 10,000 items long.
let values: &[i32] = unsafe { slice::from_raw_parts_mut(r, 10000) };
}
-
Listing 19-7: Creating a slice from an arbitrary memory
-location
+Listing 20-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
values as though it’s a valid slice results in undefined behavior.
@@ -26746,12 +26810,13 @@ language. For this, Rust has the keyword extern that facilitates th
and use of a Foreign Function Interface (FFI). An FFI is a way for a
programming language to define functions and enable a different (foreign)
programming language to call those functions.
-
Listing 19-8 demonstrates how to set up an integration with the abs function
+
Listing 20-8 demonstrates how to set up an integration with the abs function
from the C standard library. Functions declared within extern blocks are
always unsafe to call from Rust code. The reason is that other languages don’t
enforce Rust’s rules and guarantees, and Rust can’t check them, so
responsibility falls on the programmer to ensure safety.
-
Filename: src/main.rs
+
+Filename: src/main.rs
extern "C" {
fn abs(input: i32) -> i32;
}
@@ -26761,8 +26826,8 @@ fn main() {
println!("Absolute value of -3 according to C: {}", abs(-3));
}
}
-
Listing 19-8: Declaring and calling an extern function
-defined in another language
+Listing 20-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
application binary interface (ABI) the external function uses: the ABI
@@ -26795,17 +26860,18 @@ pub extern "C" fn call_from_c() {
In this book, we’ve not yet talked about global variables, which Rust does
support but can be problematic with Rust’s ownership rules. If two threads are
accessing the same mutable global variable, it can cause a data race.
-
In Rust, global variables are called static variables. Listing 19-9 shows an
+
In Rust, global variables are called static variables. Listing 20-9 shows an
example declaration and use of a static variable with a string slice as a
value.
Listing 19-9: Defining and using an immutable static
-variable
+Listing 20-9: Defining and using an immutable static variable
+
Static variables are similar to constants, which we discussed in the
“Differences Between Variables and
Constants” section
@@ -26819,9 +26885,10 @@ values in a static variable have a fixed address in memory. Using the value
will always access the same data. Constants, on the other hand, are allowed to
duplicate their data whenever they’re used. Another difference is that static
variables can be mutable. Accessing and modifying mutable static variables is
-unsafe. Listing 19-10 shows how to declare, access, and modify a mutable
+unsafe. Listing 20-10 shows how to declare, access, and modify a mutable
static variable named COUNTER.
Listing 19-10: Reading from or writing to a mutable
-static variable is unsafe
+Listing 20-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
code compiles and prints COUNTER: 3 as we would expect because it’s single
@@ -26854,7 +26921,8 @@ that data accessed from different threads is done safely.
least one of its methods has some invariant that the compiler can’t verify. We
declare that a trait is unsafe by adding the unsafe keyword before trait
and marking the implementation of the trait as unsafe too, as shown in
-Listing 19-11.
+Listing 20-11.
+
unsafe trait Foo {
// methods go here
}
@@ -26864,8 +26932,8 @@ unsafe impl Foo for i32 {
}
fn main() {}
-
Listing 19-11: Defining and implementing an unsafe
-trait
+Listing 20-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 Sync and Send marker traits we discussed in the
@@ -26911,14 +26979,15 @@ the other features discussed in this chapter.
standard library provides. The associated type is named Item and stands in
for the type of the values the type implementing the Iterator trait is
iterating over. The definition of the Iterator trait is as shown in Listing
-19-12.
+20-12.
+
Listing 19-12: The definition of the Iterator trait
-that has an associated type Item
+Listing 20-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<Self::Item>. Implementors of the
Iterator trait will specify the concrete type for Item, and the next
@@ -26928,7 +26997,8 @@ latter allow us to define a function without specifying what types it can
handle. To examine the difference between the two concepts, we’ll look at an
implementation of the Iterator trait on a type named Counter that specifies
the Item type is u32:
-
Filename: src/lib.rs
+
+Filename: src/lib.rs
struct Counter {
count: u32,
}
@@ -26952,14 +27022,16 @@ the Item type is u32:
}
}
}
+
This syntax seems comparable to that of generics. So why not just define the
-Iterator trait with generics, as shown in Listing 19-13?
+Iterator trait with generics, as shown in Listing 20-13?
+
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
+Listing A hypothetical definition of the `Iterator` trait using generics
+
+
The difference is that when using generics, as in Listing 20-13, we must
annotate the types in each implementation; because we can also implement
Iterator<String> for Counter or any other type, we could have multiple
implementations of Iterator for Counter. In other words, when a trait has a
@@ -26968,7 +27040,7 @@ the concrete types of the generic type parameters each time. When we use the
next method on Counter, we would have to provide type annotations to
indicate which implementation of Iterator we want to use.
With associated types, we don’t need to annotate types because we can’t
-implement a trait on a type multiple times. In Listing 19-12 with the
+implement a trait on a type multiple times. In Listing 20-12 with the
definition that uses associated types, we can only choose what the type of
Item will be once, because there can only be one impl Iterator for Counter.
We don’t have to specify that we want an iterator of u32 values everywhere
@@ -26988,10 +27060,11 @@ in particular situations.
Rust doesn’t allow you to create your own operators or overload arbitrary
operators. But you can overload the operations and corresponding traits listed
in std::ops by implementing the traits associated with the operator. For
-example, in Listing 19-14 we overload the + operator to add two Point
+example, in Listing 20-14 we overload the + operator to add two Point
instances together. We do this by implementing the Add trait on a Point
struct:
Listing 19-14: Implementing the Add trait to overload
-the + operator for Point instances
+Listing 20-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
associated type named Output that determines the type returned from the add
@@ -27050,8 +27123,9 @@ units. This thin wrapping of an existing type in another struct is known as the
Pattern to Implement External Traits on External Types” section. We want to add values in millimeters to values in meters and have
the implementation of Add do the conversion correctly. We can implement Add
-for Millimeters with Meters as the Rhs, as shown in Listing 19-15.
-
Filename: src/lib.rs
+for Millimeters with Meters as the Rhs, as shown in Listing 20-15.
+
+Filename: src/lib.rs
use std::ops::Add;
struct Millimeters(u32);
@@ -27064,8 +27138,8 @@ impl Add<Meters> for Millimeters {
Millimeters(self.0 + (other.0 * 1000))
}
}
-
Listing 19-15: Implementing the Add trait on
-Millimeters to add Millimeters to Meters
+Listing 20-15: Implementing the Add trait on Millimeters to add Millimeters to Meters
+
To add Millimeters and Meters, we specify impl Add<Meters> to set the
value of the Rhs type parameter instead of using the default of Self.
You’ll use default type parameters in two main ways:
@@ -27089,11 +27163,12 @@ another trait’s method, nor does Rust prevent you from implementing both trait
on one type. It’s also possible to implement a method directly on the type with
the same name as methods from traits.
When calling methods with the same name, you’ll need to tell Rust which one you
-want to use. Consider the code in Listing 19-16 where we’ve defined two traits,
+want to use. Consider the code in Listing 20-16 where we’ve defined two traits,
Pilot and Wizard, that both have a method called fly. We then implement
both traits on a type Human that already has a method named fly implemented
on it. Each fly method does something different.
-
Filename: src/main.rs
+
+Filename: src/main.rs
trait Pilot {
fn fly(&self);
}
@@ -27123,12 +27198,12 @@ impl Human {
}
fn main() {}
-
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
+Listing 20-16: Two traits are defined to have a method and are implemented on theHumantype, and aflymethod is implemented onHuman` 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.
-
Filename: src/main.rs
+the method that is directly implemented on the type, as shown in Listing 20-17.
+
+Filename: src/main.rs
trait Pilot {
fn fly(&self);
}
@@ -27161,14 +27236,15 @@ the method that is directly implemented on the type, as shown in Listing 19-17.<
let person = Human;
person.fly();
}
-
Listing 19-17: Calling fly on an instance of
-Human
+Listing 20-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.
To call the fly methods from either the Pilot trait or the Wizard trait,
we need to use more explicit syntax to specify which fly method we mean.
-Listing 19-18 demonstrates this syntax.
-
Filename: src/main.rs
+Listing 20-18 demonstrates this syntax.
+
+Filename: src/main.rs
trait Pilot {
fn fly(&self);
}
@@ -27203,12 +27279,12 @@ Listing 19-18 demonstrates this syntax.
Wizard::fly(&person);
person.fly();
}
-
Listing 19-18: Specifying which trait’s fly method we
-want to call
+Listing 20-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
Human::fly(&person), which is equivalent to the person.fly() that we used
-in Listing 19-18, but this is a bit longer to write if we don’t need to
+in Listing 20-18, but this is a bit longer to write if we don’t need to
disambiguate.
Running this code prints the following:
$ cargo run
@@ -27225,12 +27301,13 @@ trait to use based on the type of self.
However, associated functions that are not methods don’t have a self
parameter. When there are multiple types or traits that define non-method
functions with the same function name, Rust doesn’t always know which type you
-mean unless you use fully qualified syntax. For example, in Listing 19-19 we
+mean unless you use fully qualified syntax. For example, in Listing 20-19 we
create a trait for an animal shelter that wants to name all baby dogs Spot.
We make an Animal trait with an associated non-method function baby_name.
The Animal trait is implemented for the struct Dog, on which we also
provide an associated non-method function baby_name directly.
-
Filename: src/main.rs
+
+Filename: src/main.rs
trait Animal {
fn baby_name() -> String;
}
@@ -27252,9 +27329,8 @@ impl Animal for Dog {
fn main() {
println!("A baby dog is called a {}", Dog::baby_name());
}
-
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
+Listing 20-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
Animal, which describes characteristics that all animals have. Baby dogs are
@@ -27271,9 +27347,10 @@ A baby dog is called a Spot
This output isn’t what we wanted. We want to call the baby_name function that
is part of the Animal trait that we implemented on Dog so the code prints
A baby dog is called a puppy. The technique of specifying the trait name that
-we used in Listing 19-18 doesn’t help here; if we change main to the code in
-Listing 19-20, we’ll get a compilation error.
-
Filename: src/main.rs
+we used in Listing 20-18 doesn’t help here; if we change main to the code in
+Listing 20-20, we’ll get a compilation error.
+
+Filename: src/main.rs
trait Animal {
fn baby_name() -> String;
}
@@ -27295,9 +27372,8 @@ Listing 19-20, we’ll get a compilation error.
fn main() {
println!("A baby dog is called a {}", Animal::baby_name());
}
-
Listing 19-20: Attempting to call the baby_name
-function from the Animal trait, but Rust doesn’t know which implementation to
-use
+Listing 20-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
implementation of Animal::baby_name we want. We’ll get this compiler error:
@@ -27322,9 +27398,10 @@ error: could not compile `traits-example` (bin "traits-example") due to 1 previo
To disambiguate and tell Rust that we want to use the implementation of
Animal for Dog as opposed to the implementation of Animal for some other
-type, we need to use fully qualified syntax. Listing 19-21 demonstrates how to
+type, we need to use fully qualified syntax. Listing 20-21 demonstrates how to
use fully qualified syntax.
-
Filename: src/main.rs
+
+Filename: src/main.rs
trait Animal {
fn baby_name() -> String;
}
@@ -27346,9 +27423,8 @@ use fully qualified syntax.
fn main() {
println!("A baby dog is called a {}", <Dog as Animal>::baby_name());
}
-
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
+Listing 20-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
implemented on Dog by saying that we want to treat the Dog type as an
@@ -27391,9 +27467,10 @@ should print the following:
OutlinePrint trait will work only for types that also implement Display and
provide the functionality that OutlinePrint needs. We can do that in the
trait definition by specifying OutlinePrint: Display. This technique is
-similar to adding a trait bound to the trait. Listing 19-22 shows an
+similar to adding a trait bound to the trait. Listing 20-22 shows an
implementation of the OutlinePrint trait.
-
Listing 19-22: Implementing the OutlinePrint trait that
-requires the functionality from Display
+Listing 20-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
that implements Display. If we tried to use to_string without adding a
@@ -27419,7 +27496,8 @@ error saying that no method named to_string was found for the type
the current scope.
Let’s see what happens when we try to implement OutlinePrint on a type that
doesn’t implement Display, such as the Point struct:
-
Filename: src/main.rs
+
+Filename: src/main.rs
use std::fmt;
trait OutlinePrint: fmt::Display {
@@ -27445,6 +27523,7 @@ impl OutlinePrint for Point {}
let p = Point { x: 1, y: 3 };
p.outline_print();
}
+
We get an error saying that Display is required but not implemented:
$ cargo run
Compiling traits-example v0.1.0 (file:///projects/traits-example)
@@ -27483,7 +27562,8 @@ error: could not compile `traits-example` (bin "traits-example") due to 2 previo
To fix this, we implement Display on Point and satisfy the constraint that
OutlinePrint requires, like so:
-
Filename: src/main.rs
+
+Filename: src/main.rs
trait OutlinePrint: fmt::Display {
fn outline_print(&self) {
let output = self.to_string();
@@ -27515,6 +27595,7 @@ impl fmt::Display for Point {
let p = Point { x: 1, y: 3 };
p.outline_print();
}
+
Then implementing the OutlinePrint trait on Point will compile
successfully, and we can call outline_print on a Point instance to display
it within an outline of asterisks.
@@ -27536,8 +27617,9 @@ type is elided at compile time.
orphan rule prevents us from doing directly because the Display trait and the
Vec<T> type are defined outside our crate. We can make a Wrapper struct
that holds an instance of Vec<T>; then we can implement Display on
-Wrapper and use the Vec<T> value, as shown in Listing 19-23.
-
Filename: src/main.rs
+Wrapper and use the Vec<T> value, as shown in Listing 20-23.
+
+Filename: src/main.rs
use std::fmt;
struct Wrapper(Vec<String>);
@@ -27552,8 +27634,8 @@ fn main() {
let w = Wrapper(vec![String::from("hello"), String::from("world")]);
println!("w = {w}");
}
-
Listing 19-23: Creating a Wrapper type around
-Vec<String> to implement Display
+Listing 20-23: Creating a Wrapper type around Vec<String> to implement Display
+
The implementation of Display uses self.0 to access the inner Vec<T>,
because Wrapper is a tuple struct and Vec<T> is the item at index 0 in the
tuple. Then we can use the functionality of the Display trait on Wrapper.
@@ -27585,7 +27667,7 @@ Types.”
The newtype pattern is also useful for tasks beyond those we’ve discussed so
far, including statically enforcing that values are never confused and
indicating the units of a value. You saw an example of using newtypes to
-indicate units in Listing 19-15: recall that the Millimeters and Meters
+indicate units in Listing 20-15: recall that the Millimeters and Meters
structs wrapped u32 values in a newtype. If we wrote a function with a
parameter of type Millimeters, we couldn’t compile a program that
accidentally tried to call that function with a value of type Meters or a
@@ -27602,7 +27684,7 @@ internally. The newtype pattern is a lightweight way to achieve encapsulation
to hide implementation details, which we discussed in the “Encapsulation that
Hides Implementation
Details”
-section of Chapter 17.
Rust provides the ability to declare a type alias to give an existing type
another name. For this we use the type keyword. For example, we can create
@@ -27616,7 +27698,7 @@ the alias Kilometers to i32 like so:
println!("x + y = {}", x + y);
}
Now, the alias Kilometers is a synonym for i32; unlike the Millimeters
-and Meters types we created in Listing 19-15, Kilometers is not a separate,
+and Meters types we created in Listing 20-15, Kilometers is not a separate,
new type. Values that have the type Kilometers will be treated the same as
values of type i32:
fn main() {
@@ -27638,7 +27720,8 @@ might have a lengthy type like this:
Box<dyn Fn() + Send + 'static>
Writing this lengthy type in function signatures and as type annotations all
over the code can be tiresome and error prone. Imagine having a project full of
-code like that in Listing 19-24.
+code like that in Listing 20-24.
+
fn main() {
let f: Box<dyn Fn() + Send + 'static> = Box::new(|| println!("hi"));
@@ -27651,10 +27734,12 @@ code like that in Listing 19-24.
Box::new(|| ())
}
}
-
Listing 19-24: Using a long type in many places
+Listing 20-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
+Listing 20-25, we’ve introduced an alias named Thunk for the verbose type and
can replace all uses of the type with the shorter alias Thunk.
+
fn main() {
type Thunk = Box<dyn Fn() + Send + 'static>;
@@ -27669,8 +27754,8 @@ can replace all uses of the type with the shorter alias Thunk.
Box::new(|| ())
}
}
-
Listing 19-25: Introducing a type alias Thunk to reduce
-repetition
+Listing 20-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
to be evaluated at a later time, so it’s an appropriate name for a closure that
@@ -27738,7 +27823,8 @@ never are called diverging functions. We can’t create values of the t
so bar can never possibly return.
But what use is a type you can never create values for? Recall the code from
Listing 2-5, part of the number guessing game; we’ve reproduced a bit of it
-here in Listing 19-26.
+here in Listing 20-26.
+
use rand::Rng;
use std::cmp::Ordering;
use std::io;
@@ -27780,8 +27866,8 @@ here in Listing 19-26.
}
}
}
-
Listing 19-26: A match with an arm that ends in
-continue
+Listing 20-26: A match with an arm that ends in continue
+
At the time, we skipped over some details in this code. In Chapter 6 in “The
match Control Flow Operator”
section, we discussed that match arms must all return the same type. So, for
@@ -27796,7 +27882,7 @@ example, the following code doesn’t work:
The type of guess in this code would have to be an integer and a string,
and Rust requires that guess have only one type. So what does continue
return? How were we allowed to return a u32 from one arm and have another arm
-that ends with continue in Listing 19-26?
+that ends with continue in Listing 20-26?
As you might have guessed, continue has a ! value. That is, when Rust
computes the type of guess, it looks at both match arms, the former with a
value of u32 and the latter with a ! value. Because ! can never have a
@@ -27824,7 +27910,7 @@ this definition:
}
}
}
-
In this code, the same thing happens as in the match in Listing 19-26: Rust
+
In this code, the same thing happens as in the match in Listing 20-26: Rust
sees that val has the type T and panic! has the type !, so the result
of the overall match expression is T. This code works because panic!
doesn’t produce a value; it ends the program. In the None case, we won’t be
@@ -27877,7 +27963,7 @@ types behind a pointer of some kind.
We can combine str with all kinds of pointers: for example, Box<str> or
Rc<str>. In fact, you’ve seen this before but with a different dynamically
sized type: traits. Every trait is a dynamically sized type we can refer to by
-using the name of the trait. In Chapter 17 in the “Using Trait Objects That
+using the name of the trait. In Chapter 18 in the “Using Trait Objects That
Allow for Values of Different
Types” section, we mentioned that to use traits as trait objects, we must
@@ -27920,14 +28006,15 @@ closure trait. The fn type is called a function pointer. P
with function pointers will allow you to use functions as arguments to other
functions.
The syntax for specifying that a parameter is a function pointer is similar to
-that of closures, as shown in Listing 19-27, where we’ve defined a function
+that of closures, as shown in Listing 20-27, where we’ve defined a function
add_one that adds one to its parameter. The function do_twice takes two
parameters: a function pointer to any function that takes an i32 parameter
and returns an i32, and one i32 value. The do_twice function calls the
function f twice, passing it the arg value, then adds the two function call
results together. The main function calls do_twice with the arguments
add_one and 5.
Listing 19-27: Using the fn type to accept a function
-pointer as an argument
+Listing 20-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
i32. We can then call f in the body of do_twice. In main, we can pass
@@ -28038,7 +28125,7 @@ We can use a trait object:
We’ve used macros like println! throughout this book, but we haven’t fully
@@ -28103,8 +28190,9 @@ integers:
We could also use the vec! macro to make a vector of two integers or a vector
of five string slices. We wouldn’t be able to use a function to do the same
because we wouldn’t know the number or type of values up front.
-
Listing 19-28 shows a slightly simplified definition of the vec! macro.
-
Filename: src/lib.rs
+
Listing 20-28 shows a slightly simplified definition of the vec! macro.
Listing 19-28: A simplified version of the vec! macro
-definition
+Listing 20-28: A simplified version of the vec! macro definition
+
Note: The actual definition of the vec! macro in the standard library
includes code to preallocate the correct amount of memory up front. That code
@@ -28138,9 +28226,9 @@ is the only pattern in this macro, there is only one valid way to match; any
other pattern will result in an error. More complex macros will have more than
one arm.
Valid pattern syntax in macro definitions is different than the pattern syntax
-covered in Chapter 18 because macro patterns are matched against Rust code
+covered in Chapter 19 because macro patterns are matched against Rust code
structure rather than values. Let’s walk through what the pattern pieces in
-Listing 19-28 mean; for the full macro pattern syntax, see the Rust
+Listing 20-28 mean; for the full macro pattern syntax, see the Rust
Reference.
First, we use a set of parentheses to encompass the whole pattern. We use a
dollar sign ($) to declare a variable in the macro system that will contain
@@ -28181,17 +28269,18 @@ macros do. The three kinds of procedural macros are custom derive,
attribute-like, and function-like, and all work in a similar fashion.
When creating procedural macros, the definitions must reside in their own crate
with a special crate type. This is for complex technical reasons that we hope
-to eliminate in the future. In Listing 19-29, we show how to define a
+to eliminate in the future. In Listing 20-29, we show how to define a
procedural macro, where some_attribute is a placeholder for using a specific
macro variety.
Listing 19-29: An example of defining a procedural
-macro
+Listing 20-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
the proc_macro crate that is included with Rust and represents a sequence of
@@ -28211,8 +28300,9 @@ we’ll provide a procedural macro so users can annotate their type with
#[derive(HelloMacro)] to get a default implementation of the hello_macro
function. The default implementation will print Hello, Macro! My name is TypeName! where TypeName is the name of the type on which this trait has
been defined. In other words, we’ll write a crate that enables another
-programmer to write code like Listing 19-30 using our crate.
-
Filename: src/main.rs
+programmer to write code like Listing 20-30 using our crate.
+
+Filename: src/main.rs
use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;
@@ -28222,17 +28312,19 @@ struct Pancakes;
fn main() {
Pancakes::hello_macro();
}
-
Listing 19-30: The code a user of our crate will be able
-to write when using our procedural macro
+Listing 20-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:
$ cargo new hello_macro --lib
Next, we’ll define the HelloMacro trait and its associated function:
-
Filename: src/lib.rs
+
+Filename: src/lib.rs
pub trait HelloMacro {
fn hello_macro();
}
+
We have a trait and its function. At this point, our crate user could implement
the trait to achieve the desired functionality, like so:
use hello_macro::HelloMacro;
@@ -28277,7 +28369,8 @@ possible for programmers to use hello_macro even if they don’t wa
We’ll also need functionality from the syn and quote crates, as you’ll see
in a moment, so we need to add them as dependencies. Add the following to the
Cargo.toml file for hello_macro_derive:
-
To start defining the procedural macro, place the code in Listing 19-31 into
+
+
To start defining the procedural macro, place the code in Listing 20-31 into
your src/lib.rs file for the hello_macro_derive crate. Note that this code
won’t compile until we add a definition for the impl_hello_macro function.
-
Filename: hello_macro_derive/src/lib.rs
+
+Filename: hello_macro_derive/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
@@ -28301,8 +28396,8 @@ pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
// Build the trait implementation
impl_hello_macro(&ast)
}
-
Listing 19-31: Code that most procedural macro crates
-will require in order to process Rust code
+Listing 20-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
function, which is responsible for transforming the syntax tree: this makes
@@ -28329,8 +28424,9 @@ convention most procedural macros follow.
TokenStream to a data structure that we can then interpret and perform
operations on. This is where syn comes into play. The parse function in
syn takes a TokenStream and returns a DeriveInput struct representing the
-parsed Rust code. Listing 19-32 shows the relevant parts of the DeriveInput
+parsed Rust code. Listing 20-32 shows the relevant parts of the DeriveInput
struct we get from parsing the struct Pancakes; string:
+
DeriveInput {
// --snip--
@@ -28348,8 +28444,8 @@ struct we get from parsing the struct Pancakes; string:
}
)
}
-
Listing 19-32: The DeriveInput instance we get when
-parsing the code that has the macro’s attribute in Listing 19-30
+Listing 20-32: The DeriveInput instance we get when parsing the code that has the macro’s attribute in Listing 20-30
+
Now that we have the code to turn the annotated Rust code from a TokenStream
into a DeriveInput instance, let’s generate the code that implements the
-HelloMacro trait on the annotated type, as shown in Listing 19-33.
-
Filename: hello_macro_derive/src/lib.rs
+HelloMacro trait on the annotated type, as shown in Listing 20-33.
+
+Filename: hello_macro_derive/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
@@ -28395,15 +28492,15 @@ into a DeriveInput instance, let’s generate the code that impleme
};
gen.into()
}
-
Listing 19-33: Implementing the HelloMacro trait using
-the parsed Rust code
+Listing 20-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
-we run the impl_hello_macro function on the code in Listing 19-30, the
+annotated type using ast.ident. The struct in Listing 20-32 shows that when
+we run the impl_hello_macro function on the code in Listing 20-30, the
ident we get will have the ident field with a value of "Pancakes". Thus,
-the name variable in Listing 19-33 will contain an Ident struct instance
+the name variable in Listing 20-33 will contain an Ident struct instance
that, when printed, will be the string "Pancakes", the name of the struct in
-Listing 19-30.
+Listing 20-30.
The quote! macro lets us define the Rust code that we want to return. The
compiler expects something different to the direct result of the quote!
macro’s execution, so we need to convert it to a TokenStream. We do this by
@@ -28427,7 +28524,7 @@ expression to print literally, so we use stringify!. Using st
saves an allocation by converting #name to a string literal at compile time.
At this point, cargo build should complete successfully in both hello_macro
and hello_macro_derive. Let’s hook up these crates to the code in Listing
-19-30 to see the procedural macro in action! Create a new binary project in
+20-30 to see the procedural macro in action! Create a new binary project in
your projects directory using cargo new pancakes. We need to add
hello_macro and hello_macro_derive as dependencies in the pancakes
crate’s Cargo.toml. If you’re publishing your versions of hello_macro and
@@ -28436,7 +28533,7 @@ dependencies; if not, you can specify them as path dependencies as
Put the code in Listing 19-30 into src/main.rs, and run cargo run: it
+
Put the code in Listing 20-30 into src/main.rs, and run cargo run: it
should print Hello, Macro! My name is Pancakes! The implementation of the
HelloMacro trait from the procedural macro was included without the
pancakes crate needing to implement it; the #[derive(HelloMacro)] added the
@@ -28543,10 +28640,11 @@ this. Let’s make a new project in the usual fashion:
Created binary (application) `hello` project
$ cd hello
-
Now enter the code in Listing 20-1 in src/main.rs to start. This code will
+
Now enter the code in Listing 21-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!.
Listing 20-1: Listening for incoming streams and printing
-a message when we receive a stream
+Listing 21-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. In the address, the section before the colon is an IP address
representing your computer (this is the same on every computer and doesn’t
@@ -28630,8 +28728,9 @@ separate the concerns of first getting a connection and then taking some action
with the connection, we’ll start a new function for processing connections. In
this new handle_connection function, we’ll read data from the TCP stream and
print it so we can see the data being sent from the browser. Change the code to
-look like Listing 20-2.
Listing 20-2: Reading from the TcpStream and printing
-the data
+Listing 21-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. In the for
loop in the main function, instead of printing a message that says we made a
@@ -28764,8 +28863,9 @@ response.
successful HTTP response. Let’s write this to the stream as our response to a
successful request! From the handle_connection function, remove the
println! that was printing the request data and replace it with the code in
-Listing 20-3.
-
Listing 20-3: Writing a tiny successful HTTP response to
-the stream
+Listing 21-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. Then we call as_bytes on our response to convert the string
data to bytes. The write_all method on stream takes a &[u8] and sends
@@ -28809,9 +28909,10 @@ request and sending a response!
Let’s implement the functionality for returning more than a blank page. Create
the new file hello.html in the root of your project directory, not in the
-src directory. You can input any HTML you want; Listing 20-4 shows one
+src directory. You can input any HTML you want; Listing 21-4 shows one
possibility.
Listing 20-4: A sample HTML file to return in a
-response
+Listing 21-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
-shown in Listing 20-5 to read the HTML file, add it to the response as a body,
+shown in Listing 21-5 to read the HTML file, add it to the response as a body,
and send it.
-
Filename: src/main.rs
+
+Filename: src/main.rs
use std::{
fs,
io::{prelude::*, BufReader},
@@ -28865,8 +28967,8 @@ and send it.
stream.write_all(response.as_bytes()).unwrap();
}
-
Listing 20-5: Sending the contents of hello.html as the
-body of the response
+Listing 21-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. The code for reading the contents of a file to a
string should look familiar; we used it in Chapter 12 when we read the contents
@@ -28889,10 +28991,11 @@ request to /.
client requested. Let’s add functionality to check that the browser is
requesting / before returning the HTML file and return an error if the
browser requests anything else. For this we need to modify handle_connection,
-as shown in Listing 20-6. This new code checks the content of the request
+as shown in Listing 21-6. This new code checks the content of the request
received against what we know a request for / looks like and adds if and
else blocks to treat requests differently.
-
Filename: src/main.rs
+
+Filename: src/main.rs
use std::{
fs,
io::{prelude::*, BufReader},
@@ -28928,14 +29031,14 @@ fn handle_connection(mut stream: TcpStream) {
// some other request
}
}
-
Listing 20-6: Handling requests to / differently from
-other requests
+Listing 21-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
first item from the iterator. The first unwrap takes care of the Option and
stops the program if the iterator has no items. The second unwrap handles the
Result and has the same effect as the unwrap that was in the map added in
-Listing 20-2.
+Listing 21-2.
Next, we check the request_line to see if it equals the request line of a GET
request to the / path. If it does, the if block returns the contents of our
HTML file.
@@ -28945,12 +29048,13 @@ a moment to respond to all other requests.
Run this code now and request 127.0.0.1:7878; you should get the HTML in
hello.html. If you make any other request, such as
127.0.0.1:7878/something-else, you’ll get a connection error like those you
-saw when running the code in Listing 20-1 and Listing 20-2.
-
Now let’s add the code in Listing 20-7 to the else block to return a response
+saw when running the code in Listing 21-1 and Listing 21-2.
+
Now let’s add the code in Listing 21-7 to the else block to return a response
with the status code 404, which signals that the content for the request was
not found. We’ll also return some HTML for a page to render in the browser
indicating the response to the end user.
-
Filename: src/main.rs
+
+Filename: src/main.rs
use std::{
fs,
io::{prelude::*, BufReader},
@@ -28994,14 +29098,15 @@ indicating the response to the end user.
stream.write_all(response.as_bytes()).unwrap();
}
}
-
Listing 20-7: Responding with status code 404 and an
-error page if anything other than / was requested
+Listing 21-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. The body of the response will be the HTML in the file 404.html.
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.
Listing 20-8: Sample content for the page to send back
-with any 404 response
+Listing 21-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
127.0.0.1:7878/foo, should return the error HTML from 404.html.
@@ -29026,9 +29131,10 @@ differences are the status line and the filename. Let’s make the code more
concise by pulling out those differences into separate if and else lines
that will assign the values of the status line and the filename to variables;
we can then use those variables unconditionally in the code to read the file
-and write the response. Listing 20-9 shows the resulting code after replacing
+and write the response. Listing 21-9 shows the resulting code after replacing
the large if and else blocks.
-
Listing 20-9: Refactoring the if and else blocks to
-contain only the code that differs between the two cases
+Listing 21-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
two values to status_line and filename using a pattern in the let
-statement, as discussed in Chapter 18.
+statement, as discussed in Chapter 19.
The previously duplicated code is now outside the if and else blocks and
uses the status_line and filename variables. This makes it easier to see
the difference between the two cases, and it means we have only one place to
update the code if we want to change how the file reading and response writing
-work. The behavior of the code in Listing 20-9 will be the same as that in
-Listing 20-7.
+work. The behavior of the code in Listing 21-9 will be the same as that in
+Listing 21-7.
Awesome! We now have a simple web server in approximately 40 lines of Rust code
that responds to one request with a page of content and responds to all other
requests with a 404 response.
@@ -29094,10 +29200,11 @@ finished, even if the new requests can be processed quickly. We’ll need to fix
this, but first, we’ll look at the problem in action.
We’ll look at how a slow-processing request can affect other requests made to
-our current server implementation. Listing 20-10 implements handling a request
+our current server implementation. Listing 21-10 implements handling a request
to /sleep with a simulated slow response that will cause the server to sleep
for 5 seconds before responding.
Listing 20-10: Simulating a slow request by sleeping for
-5 seconds
+Listing 21-10: Simulating a slow request by sleeping for 5 seconds
+
We switched from if to match now that we have three cases. We need to
explicitly match on a slice of request_line to pattern match against the
string literal values; match doesn’t do automatic referencing and
dereferencing like the equality method does.
-
The first arm is the same as the if block from Listing 20-9. The second arm
+
The first arm is the same as the if block from Listing 21-9. The second arm
matches a request to /sleep. When that request is received, the server will
sleep for 5 seconds before rendering the successful HTML page. The third arm is
-the same as the else block from Listing 20-9.
+the same as the else block from Listing 21-9.
You can see how primitive our server is: real libraries would handle the
recognition of multiple requests in a much less verbose way!
Start the server using cargo run. Then open two browser windows: one for
@@ -29209,9 +29316,10 @@ every connection. As mentioned earlier, this isn’t our final plan due to the
problems with potentially spawning an unlimited number of threads, but it is a
starting point to get a working multithreaded server first. Then we’ll add the
thread pool as an improvement, and contrasting the two solutions will be
-easier. Listing 20-11 shows the changes to make to main to spawn a new thread
+easier. Listing 21-11 shows the changes to make to main to spawn a new thread
to handle each stream within the for loop.
-
Filename: src/main.rs
+
+Filename: src/main.rs
use std::{
fs,
io::{prelude::*, BufReader},
@@ -29253,8 +29361,8 @@ to handle each stream within the for loop.
stream.write_all(response.as_bytes()).unwrap();
}
-
Listing 20-11: Spawning a new thread for each
-stream
+Listing 21-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
/sleep in your browser, then / in two more browser tabs, you’ll indeed see
@@ -29266,9 +29374,10 @@ new threads without any limit.
We want our thread pool to work in a similar, familiar way so switching from
threads to a thread pool doesn’t require large changes to the code that uses
-our API. Listing 20-12 shows the hypothetical interface for a ThreadPool
+our API. Listing 21-12 shows the hypothetical interface for a ThreadPool
struct we want to use instead of thread::spawn.
-
Filename: src/main.rs
+
+Filename: src/main.rs
use std::{
fs,
io::{prelude::*, BufReader},
@@ -29311,7 +29420,8 @@ struct we want to use instead of thread::spawn.
stream.write_all(response.as_bytes()).unwrap();
}
-
Listing 20-12: Our ideal ThreadPool interface
+Listing 21-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. Then, in the for loop, pool.execute has a
similar interface as thread::spawn in that it takes a closure the pool should
@@ -29321,7 +29431,7 @@ compile, but we’ll try so the compiler can guide us in how to fix it.
Make the changes in Listing 20-12 to src/main.rs, and then let’s use the
+
Make the changes in Listing 21-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:
$ cargo check
@@ -29344,11 +29454,14 @@ library for any work we want to do using a thread pool, not just for serving
web requests.
Create a src/lib.rs that contains the following, which is the simplest
definition of a ThreadPool struct that we can have for now:
-
Filename: src/lib.rs
+
+Filename: src/lib.rs
pub struct ThreadPool;
+
Then edit main.rs file to bring ThreadPool into scope from the library
crate by adding the following code to the top of src/main.rs:
-
Filename: src/main.rs
+
+Filename: src/main.rs
use hello::ThreadPool;
use std::{
fs,
@@ -29392,6 +29505,7 @@ crate by adding the following code to the top of src/main.rs:
stream.write_all(response.as_bytes()).unwrap();
}
+
This code still won’t work, but let’s check it again to get the next error that
we need to address:
$ cargo check
@@ -29410,7 +29524,8 @@ error: could not compile `hello` (bin "hello") due to 1 previous error
that can accept 4 as an argument and should return a ThreadPool instance.
Let’s implement the simplest new function that will have those
characteristics:
-
We chose usize as the type of the size parameter, because we know that a
negative number of threads doesn’t make any sense. We also know we’ll use this
4 as the number of elements in a collection of threads, which is what the
@@ -29466,7 +29582,8 @@ request’s closure one time, which matches the Once in FnOnc
closure from one thread to another and 'static because we don’t know how long
the thread will take to execute. Let’s create an execute method on
ThreadPool that will take a generic parameter of type F with these bounds:
-
Filename: src/lib.rs
+
+Filename: src/lib.rs
pub struct ThreadPool;
impl ThreadPool {
@@ -29481,6 +29598,7 @@ the thread will take to execute. Let’s create an execute method o
{
}
}
+
We still use the () after FnOnce because this FnOnce represents a closure
that takes no parameters and returns the unit type (). Just like function
definitions, the return type can be omitted from the signature, but even if we
@@ -29511,8 +29629,9 @@ parameter, because a pool with a negative number of threads makes no sense.
However, a pool with zero threads also makes no sense, yet zero is a perfectly
valid usize. We’ll add code to check that size is greater than zero before
we return a ThreadPool instance and have the program panic if it receives a
-zero by using the assert! macro, as shown in Listing 20-13.
-
Filename: src/lib.rs
+zero by using the assert! macro, as shown in Listing 21-13.
+
+Filename: src/lib.rs
pub struct ThreadPool;
impl ThreadPool {
@@ -29536,8 +29655,8 @@ zero by using the assert! macro, as shown in Listing 20-13.
{
}
}
-
Listing 20-13: Implementing ThreadPool::new to panic if
-size is zero
+Listing 21-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, as discussed in
@@ -29564,12 +29683,13 @@ look at the thread::spawn signature:
closure returns. Let’s try using JoinHandle too and see what happens. In our
case, the closures we’re passing to the thread pool will handle the connection
and not return anything, so T will be the unit type ().
-
The code in Listing 20-14 will compile but doesn’t create any threads yet.
+
The code in Listing 21-14 will compile but doesn’t create any threads yet.
We’ve changed the definition of ThreadPool to hold a vector of
thread::JoinHandle<()> instances, initialized the vector with a capacity of
size, set up a for loop that will run some code to create the threads, and
returned a ThreadPool instance containing them.
Listing 20-14: Creating a vector for ThreadPool to hold
-the threads
+Listing 21-14: Creating a vector for ThreadPool to hold the threads
+
We’ve brought std::thread into scope in the library crate, because we’re
using thread::JoinHandle as the type of the items in the vector in
ThreadPool.
@@ -29617,7 +29737,7 @@ this allocation up front is slightly more efficient than using Vec::new
When you run cargo check again, it should succeed.
We left a comment in the for loop in Listing 20-14 regarding the creation of
+
We left a comment in the for loop in Listing 21-14 regarding the creation of
threads. Here, we’ll look at how we actually create threads. The standard
library provides thread::spawn as a way to create threads, and
thread::spawn expects to get some code the thread should run as soon as the
@@ -29651,9 +29771,10 @@ closure.
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.
-
Ready? Here is Listing 20-15 with one way to make the preceding modifications.
-
Filename: src/lib.rs
+looking at the code in Listing 21-15.
+
Ready? Here is Listing 21-15 with one way to make the preceding modifications.
Listing 20-15: Modifying ThreadPool to hold Worker
-instances instead of holding threads directly
+Listing 21-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<()>
instances. We use the counter in the for loop as an argument to
@@ -29746,10 +29867,11 @@ sender.
closures of any jobs it receives.
Let’s start by creating a channel in ThreadPool::new and holding the sender
-in the ThreadPool instance, as shown in Listing 20-16. The Job struct
+in the ThreadPool instance, as shown in Listing 21-16. The Job struct
doesn’t hold anything for now but will be the type of item we’re sending down
the channel.
Listing 20-16: Modifying ThreadPool to store the
-sender of a channel that transmits Job instances
+Listing 21-16: Modifying ThreadPool to store the sender of a channel that transmits Job instances
+
In ThreadPool::new, we create our new channel and have the pool hold the
sender. This will successfully compile.
Let’s try passing a receiver of the channel into each worker as the thread pool
creates the channel. We know we want to use the receiver in the thread that the
workers spawn, so we’ll reference the receiver parameter in the closure. The
-code in Listing 20-17 won’t quite compile yet.
Listing 20-17: Passing the receiver to the workers
+Listing 21-17: Passing the receiver to the workers
+
We’ve made some small and straightforward changes: we pass the receiver into
Worker::new, and then we use it inside the closure.
When we try to check this code, we get this error:
@@ -29913,8 +30037,9 @@ otherwise, we might get race conditions (as covered in Chapter 16).
ownership across multiple threads and allow the threads to mutate the value, we
need to use Arc<Mutex<T>>. 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.
-
Filename: src/lib.rs
+receiver at a time. Listing 21-18 shows the changes we need to make.
+
+Filename: src/lib.rs
use std::{
sync::{mpsc, Arc, Mutex},
thread,
@@ -29979,8 +30104,8 @@ receiver at a time. Listing 20-18 shows the changes we need to make.
Worker { id, thread }
}
}
-
Listing 20-18: Sharing the receiver among the workers
-using Arc and Mutex
+Listing 21-18: Sharing the receiver among the workers using Arc and Mutex
+
In ThreadPool::new, we put the receiver in an Arc and a Mutex. For each
new worker, we clone the Arc to bump the reference count so the workers can
share ownership of the receiver.
@@ -29990,9 +30115,10 @@ share ownership of the receiver.
Job from a struct to a type alias for a trait object that holds the type of
closure that execute receives. As discussed in the “Creating Type Synonyms
with Type Aliases”
-section of Chapter 19, type aliases allow us to make long types shorter for
-ease of use. Look at Listing 20-19.
-
Filename: src/lib.rs
+section of Chapter 20, type aliases allow us to make long types shorter for
+ease of use. Look at Listing 21-19.
+
+Filename: src/lib.rs
Listing 20-19: Creating a Job type alias for a Box
-that holds each closure and then sending the job down the channel
+Listing 21-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, we
send that job down the sending end of the channel. We’re calling unwrap on
send for the case that sending fails. This might happen if, for example, we
@@ -30072,8 +30198,9 @@ compiler doesn’t know that.
thread::spawn still only references the receiving end of the channel.
Instead, we need the closure to loop forever, asking the receiving end of the
channel for a job and running the job when it gets one. Let’s make the change
-shown in Listing 20-20 to Worker::new.
-
Filename: src/lib.rs
+shown in Listing 21-20 to Worker::new.
+
+Filename: src/lib.rs
Listing 20-20: Receiving and executing the jobs in the
-worker’s thread
+Listing 21-20: Receiving and executing the jobs in the worker’s thread
+
Here, we first call lock on the receiver to acquire the mutex, and then we
call unwrap to panic on any errors. Acquiring a lock might fail if the mutex
is in a poisoned state, which can happen if some other thread panicked while
@@ -30159,7 +30286,7 @@ wait until a job becomes available. The Mutex<T> ensures that
Our thread pool is now in a working state! Give it a cargo run and make some
requests:
section of Chapter 19.
+“Macros” section of Chapter 20.
The Debug trait enables debug formatting in format strings, which you
indicate by adding :? within {} placeholders.
diff --git a/searchindex.js b/searchindex.js
index 4e41cf408..4f2b6d6e2 100644
--- a/searchindex.js
+++ b/searchindex.js
@@ -1 +1 @@
-Object.assign(window.search, {"doc_urls":["title-page.html#the-rust-programming-language","foreword.html#foreword","ch00-00-introduction.html#introduction","ch00-00-introduction.html#who-rust-is-for","ch00-00-introduction.html#teams-of-developers","ch00-00-introduction.html#students","ch00-00-introduction.html#companies","ch00-00-introduction.html#open-source-developers","ch00-00-introduction.html#people-who-value-speed-and-stability","ch00-00-introduction.html#who-this-book-is-for","ch00-00-introduction.html#how-to-use-this-book","ch00-00-introduction.html#source-code","ch01-00-getting-started.html#getting-started","ch01-01-installation.html#installation","ch01-01-installation.html#command-line-notation","ch01-01-installation.html#installing-rustup-on-linux-or-macos","ch01-01-installation.html#installing-rustup-on-windows","ch01-01-installation.html#troubleshooting","ch01-01-installation.html#updating-and-uninstalling","ch01-01-installation.html#local-documentation","ch01-02-hello-world.html#hello-world","ch01-02-hello-world.html#creating-a-project-directory","ch01-02-hello-world.html#writing-and-running-a-rust-program","ch01-02-hello-world.html#anatomy-of-a-rust-program","ch01-02-hello-world.html#compiling-and-running-are-separate-steps","ch01-03-hello-cargo.html#hello-cargo","ch01-03-hello-cargo.html#creating-a-project-with-cargo","ch01-03-hello-cargo.html#building-and-running-a-cargo-project","ch01-03-hello-cargo.html#building-for-release","ch01-03-hello-cargo.html#cargo-as-convention","ch01-03-hello-cargo.html#summary","ch02-00-guessing-game-tutorial.html#programming-a-guessing-game","ch02-00-guessing-game-tutorial.html#setting-up-a-new-project","ch02-00-guessing-game-tutorial.html#processing-a-guess","ch02-00-guessing-game-tutorial.html#storing-values-with-variables","ch02-00-guessing-game-tutorial.html#receiving-user-input","ch02-00-guessing-game-tutorial.html#handling-potential-failure-with-result","ch02-00-guessing-game-tutorial.html#printing-values-with-println-placeholders","ch02-00-guessing-game-tutorial.html#testing-the-first-part","ch02-00-guessing-game-tutorial.html#generating-a-secret-number","ch02-00-guessing-game-tutorial.html#using-a-crate-to-get-more-functionality","ch02-00-guessing-game-tutorial.html#generating-a-random-number","ch02-00-guessing-game-tutorial.html#comparing-the-guess-to-the-secret-number","ch02-00-guessing-game-tutorial.html#allowing-multiple-guesses-with-looping","ch02-00-guessing-game-tutorial.html#quitting-after-a-correct-guess","ch02-00-guessing-game-tutorial.html#handling-invalid-input","ch02-00-guessing-game-tutorial.html#summary","ch03-00-common-programming-concepts.html#common-programming-concepts","ch03-01-variables-and-mutability.html#variables-and-mutability","ch03-01-variables-and-mutability.html#constants","ch03-01-variables-and-mutability.html#shadowing","ch03-02-data-types.html#data-types","ch03-02-data-types.html#scalar-types","ch03-02-data-types.html#compound-types","ch03-03-how-functions-work.html#functions","ch03-03-how-functions-work.html#parameters","ch03-03-how-functions-work.html#statements-and-expressions","ch03-03-how-functions-work.html#functions-with-return-values","ch03-04-comments.html#comments","ch03-05-control-flow.html#control-flow","ch03-05-control-flow.html#if-expressions","ch03-05-control-flow.html#repetition-with-loops","ch03-05-control-flow.html#summary","ch04-00-understanding-ownership.html#understanding-ownership","ch04-01-what-is-ownership.html#what-is-ownership","ch04-01-what-is-ownership.html#the-stack-and-the-heap","ch04-01-what-is-ownership.html#ownership-rules","ch04-01-what-is-ownership.html#variable-scope","ch04-01-what-is-ownership.html#the-string-type","ch04-01-what-is-ownership.html#memory-and-allocation","ch04-01-what-is-ownership.html#ownership-and-functions","ch04-01-what-is-ownership.html#return-values-and-scope","ch04-02-references-and-borrowing.html#references-and-borrowing","ch04-02-references-and-borrowing.html#mutable-references","ch04-02-references-and-borrowing.html#dangling-references","ch04-02-references-and-borrowing.html#the-rules-of-references","ch04-03-slices.html#the-slice-type","ch04-03-slices.html#string-slices","ch04-03-slices.html#other-slices","ch04-03-slices.html#summary","ch05-00-structs.html#using-structs-to-structure-related-data","ch05-01-defining-structs.html#defining-and-instantiating-structs","ch05-01-defining-structs.html#using-the-field-init-shorthand","ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax","ch05-01-defining-structs.html#using-tuple-structs-without-named-fields-to-create-different-types","ch05-01-defining-structs.html#unit-like-structs-without-any-fields","ch05-01-defining-structs.html#ownership-of-struct-data","ch05-02-example-structs.html#an-example-program-using-structs","ch05-02-example-structs.html#refactoring-with-tuples","ch05-02-example-structs.html#refactoring-with-structs-adding-more-meaning","ch05-02-example-structs.html#adding-useful-functionality-with-derived-traits","ch05-03-method-syntax.html#method-syntax","ch05-03-method-syntax.html#defining-methods","ch05-03-method-syntax.html#wheres-the---operator","ch05-03-method-syntax.html#methods-with-more-parameters","ch05-03-method-syntax.html#associated-functions","ch05-03-method-syntax.html#multiple-impl-blocks","ch05-03-method-syntax.html#summary","ch06-00-enums.html#enums-and-pattern-matching","ch06-01-defining-an-enum.html#defining-an-enum","ch06-01-defining-an-enum.html#enum-values","ch06-01-defining-an-enum.html#the-option-enum-and-its-advantages-over-null-values","ch06-02-match.html#the-match-control-flow-construct","ch06-02-match.html#patterns-that-bind-to-values","ch06-02-match.html#matching-with-option","ch06-02-match.html#matches-are-exhaustive","ch06-02-match.html#catch-all-patterns-and-the-_-placeholder","ch06-03-if-let.html#concise-control-flow-with-if-let","ch06-03-if-let.html#summary","ch07-00-managing-growing-projects-with-packages-crates-and-modules.html#managing-growing-projects-with-packages-crates-and-modules","ch07-01-packages-and-crates.html#packages-and-crates","ch07-02-defining-modules-to-control-scope-and-privacy.html#defining-modules-to-control-scope-and-privacy","ch07-02-defining-modules-to-control-scope-and-privacy.html#modules-cheat-sheet","ch07-02-defining-modules-to-control-scope-and-privacy.html#grouping-related-code-in-modules","ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#paths-for-referring-to-an-item-in-the-module-tree","ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#exposing-paths-with-the-pub-keyword","ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#starting-relative-paths-with-super","ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#making-structs-and-enums-public","ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#bringing-paths-into-scope-with-the-use-keyword","ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#creating-idiomatic-use-paths","ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#providing-new-names-with-the-as-keyword","ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#re-exporting-names-with-pub-use","ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#using-external-packages","ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#using-nested-paths-to-clean-up-large-use-lists","ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#the-glob-operator","ch07-05-separating-modules-into-different-files.html#separating-modules-into-different-files","ch07-05-separating-modules-into-different-files.html#alternate-file-paths","ch07-05-separating-modules-into-different-files.html#summary","ch08-00-common-collections.html#common-collections","ch08-01-vectors.html#storing-lists-of-values-with-vectors","ch08-01-vectors.html#creating-a-new-vector","ch08-01-vectors.html#updating-a-vector","ch08-01-vectors.html#reading-elements-of-vectors","ch08-01-vectors.html#iterating-over-the-values-in-a-vector","ch08-01-vectors.html#using-an-enum-to-store-multiple-types","ch08-01-vectors.html#dropping-a-vector-drops-its-elements","ch08-02-strings.html#storing-utf-8-encoded-text-with-strings","ch08-02-strings.html#what-is-a-string","ch08-02-strings.html#creating-a-new-string","ch08-02-strings.html#updating-a-string","ch08-02-strings.html#indexing-into-strings","ch08-02-strings.html#slicing-strings","ch08-02-strings.html#methods-for-iterating-over-strings","ch08-02-strings.html#strings-are-not-so-simple","ch08-03-hash-maps.html#storing-keys-with-associated-values-in-hash-maps","ch08-03-hash-maps.html#creating-a-new-hash-map","ch08-03-hash-maps.html#accessing-values-in-a-hash-map","ch08-03-hash-maps.html#hash-maps-and-ownership","ch08-03-hash-maps.html#updating-a-hash-map","ch08-03-hash-maps.html#hashing-functions","ch08-03-hash-maps.html#summary","ch09-00-error-handling.html#error-handling","ch09-01-unrecoverable-errors-with-panic.html#unrecoverable-errors-with-panic","ch09-01-unrecoverable-errors-with-panic.html#unwinding-the-stack-or-aborting-in-response-to-a-panic","ch09-02-recoverable-errors-with-result.html#recoverable-errors-with-result","ch09-02-recoverable-errors-with-result.html#matching-on-different-errors","ch09-02-recoverable-errors-with-result.html#propagating-errors","ch09-03-to-panic-or-not-to-panic.html#to-panic-or-not-to-panic","ch09-03-to-panic-or-not-to-panic.html#examples-prototype-code-and-tests","ch09-03-to-panic-or-not-to-panic.html#cases-in-which-you-have-more-information-than-the-compiler","ch09-03-to-panic-or-not-to-panic.html#guidelines-for-error-handling","ch09-03-to-panic-or-not-to-panic.html#creating-custom-types-for-validation","ch09-03-to-panic-or-not-to-panic.html#summary","ch10-00-generics.html#generic-types-traits-and-lifetimes","ch10-00-generics.html#removing-duplication-by-extracting-a-function","ch10-01-syntax.html#generic-data-types","ch10-01-syntax.html#in-function-definitions","ch10-01-syntax.html#in-struct-definitions","ch10-01-syntax.html#in-enum-definitions","ch10-01-syntax.html#in-method-definitions","ch10-01-syntax.html#performance-of-code-using-generics","ch10-02-traits.html#traits-defining-shared-behavior","ch10-02-traits.html#defining-a-trait","ch10-02-traits.html#implementing-a-trait-on-a-type","ch10-02-traits.html#default-implementations","ch10-02-traits.html#traits-as-parameters","ch10-02-traits.html#returning-types-that-implement-traits","ch10-02-traits.html#using-trait-bounds-to-conditionally-implement-methods","ch10-03-lifetime-syntax.html#validating-references-with-lifetimes","ch10-03-lifetime-syntax.html#preventing-dangling-references-with-lifetimes","ch10-03-lifetime-syntax.html#the-borrow-checker","ch10-03-lifetime-syntax.html#generic-lifetimes-in-functions","ch10-03-lifetime-syntax.html#lifetime-annotation-syntax","ch10-03-lifetime-syntax.html#lifetime-annotations-in-function-signatures","ch10-03-lifetime-syntax.html#thinking-in-terms-of-lifetimes","ch10-03-lifetime-syntax.html#lifetime-annotations-in-struct-definitions","ch10-03-lifetime-syntax.html#lifetime-elision","ch10-03-lifetime-syntax.html#lifetime-annotations-in-method-definitions","ch10-03-lifetime-syntax.html#the-static-lifetime","ch10-03-lifetime-syntax.html#generic-type-parameters-trait-bounds-and-lifetimes-together","ch10-03-lifetime-syntax.html#summary","ch11-00-testing.html#writing-automated-tests","ch11-01-writing-tests.html#how-to-write-tests","ch11-01-writing-tests.html#the-anatomy-of-a-test-function","ch11-01-writing-tests.html#checking-results-with-the-assert-macro","ch11-01-writing-tests.html#testing-equality-with-the-assert_eq-and-assert_ne-macros","ch11-01-writing-tests.html#adding-custom-failure-messages","ch11-01-writing-tests.html#checking-for-panics-with-should_panic","ch11-01-writing-tests.html#using-result-in-tests","ch11-02-running-tests.html#controlling-how-tests-are-run","ch11-02-running-tests.html#running-tests-in-parallel-or-consecutively","ch11-02-running-tests.html#showing-function-output","ch11-02-running-tests.html#running-a-subset-of-tests-by-name","ch11-02-running-tests.html#ignoring-some-tests-unless-specifically-requested","ch11-03-test-organization.html#test-organization","ch11-03-test-organization.html#unit-tests","ch11-03-test-organization.html#integration-tests","ch11-03-test-organization.html#summary","ch12-00-an-io-project.html#an-io-project-building-a-command-line-program","ch12-01-accepting-command-line-arguments.html#accepting-command-line-arguments","ch12-01-accepting-command-line-arguments.html#reading-the-argument-values","ch12-01-accepting-command-line-arguments.html#the-args-function-and-invalid-unicode","ch12-01-accepting-command-line-arguments.html#saving-the-argument-values-in-variables","ch12-02-reading-a-file.html#reading-a-file","ch12-03-improving-error-handling-and-modularity.html#refactoring-to-improve-modularity-and-error-handling","ch12-03-improving-error-handling-and-modularity.html#separation-of-concerns-for-binary-projects","ch12-03-improving-error-handling-and-modularity.html#the-trade-offs-of-using-clone","ch12-03-improving-error-handling-and-modularity.html#fixing-the-error-handling","ch12-03-improving-error-handling-and-modularity.html#extracting-logic-from-main","ch12-03-improving-error-handling-and-modularity.html#splitting-code-into-a-library-crate","ch12-04-testing-the-librarys-functionality.html#developing-the-librarys-functionality-with-test-driven-development","ch12-04-testing-the-librarys-functionality.html#writing-a-failing-test","ch12-04-testing-the-librarys-functionality.html#writing-code-to-pass-the-test","ch12-05-working-with-environment-variables.html#working-with-environment-variables","ch12-05-working-with-environment-variables.html#writing-a-failing-test-for-the-case-insensitive-search-function","ch12-05-working-with-environment-variables.html#implementing-the-search_case_insensitive-function","ch12-06-writing-to-stderr-instead-of-stdout.html#writing-error-messages-to-standard-error-instead-of-standard-output","ch12-06-writing-to-stderr-instead-of-stdout.html#checking-where-errors-are-written","ch12-06-writing-to-stderr-instead-of-stdout.html#printing-errors-to-standard-error","ch12-06-writing-to-stderr-instead-of-stdout.html#summary","ch13-00-functional-features.html#functional-language-features-iterators-and-closures","ch13-01-closures.html#closures-anonymous-functions-that-capture-their-environment","ch13-01-closures.html#capturing-the-environment-with-closures","ch13-01-closures.html#closure-type-inference-and-annotation","ch13-01-closures.html#capturing-references-or-moving-ownership","ch13-01-closures.html#moving-captured-values-out-of-closures-and-the-fn-traits","ch13-02-iterators.html#processing-a-series-of-items-with-iterators","ch13-02-iterators.html#the-iterator-trait-and-the-next-method","ch13-02-iterators.html#methods-that-consume-the-iterator","ch13-02-iterators.html#methods-that-produce-other-iterators","ch13-02-iterators.html#using-closures-that-capture-their-environment","ch13-03-improving-our-io-project.html#improving-our-io-project","ch13-03-improving-our-io-project.html#removing-a-clone-using-an-iterator","ch13-03-improving-our-io-project.html#making-code-clearer-with-iterator-adapters","ch13-03-improving-our-io-project.html#choosing-between-loops-or-iterators","ch13-04-performance.html#comparing-performance-loops-vs-iterators","ch13-04-performance.html#summary","ch14-00-more-about-cargo.html#more-about-cargo-and-cratesio","ch14-01-release-profiles.html#customizing-builds-with-release-profiles","ch14-02-publishing-to-crates-io.html#publishing-a-crate-to-cratesio","ch14-02-publishing-to-crates-io.html#making-useful-documentation-comments","ch14-02-publishing-to-crates-io.html#exporting-a-convenient-public-api-with-pub-use","ch14-02-publishing-to-crates-io.html#setting-up-a-cratesio-account","ch14-02-publishing-to-crates-io.html#adding-metadata-to-a-new-crate","ch14-02-publishing-to-crates-io.html#publishing-to-cratesio","ch14-02-publishing-to-crates-io.html#publishing-a-new-version-of-an-existing-crate","ch14-02-publishing-to-crates-io.html#deprecating-versions-from-cratesio-with-cargo-yank","ch14-03-cargo-workspaces.html#cargo-workspaces","ch14-03-cargo-workspaces.html#creating-a-workspace","ch14-03-cargo-workspaces.html#creating-the-second-package-in-the-workspace","ch14-04-installing-binaries.html#installing-binaries-with-cargo-install","ch14-05-extending-cargo.html#extending-cargo-with-custom-commands","ch14-05-extending-cargo.html#summary","ch15-00-smart-pointers.html#smart-pointers","ch15-01-box.html#using-box-to-point-to-data-on-the-heap","ch15-01-box.html#using-a-box-to-store-data-on-the-heap","ch15-01-box.html#enabling-recursive-types-with-boxes","ch15-02-deref.html#treating-smart-pointers-like-regular-references-with-the-deref-trait","ch15-02-deref.html#following-the-pointer-to-the-value","ch15-02-deref.html#using-box-like-a-reference","ch15-02-deref.html#defining-our-own-smart-pointer","ch15-02-deref.html#treating-a-type-like-a-reference-by-implementing-the-deref-trait","ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods","ch15-02-deref.html#how-deref-coercion-interacts-with-mutability","ch15-03-drop.html#running-code-on-cleanup-with-the-drop-trait","ch15-03-drop.html#dropping-a-value-early-with-stdmemdrop","ch15-04-rc.html#rc-the-reference-counted-smart-pointer","ch15-04-rc.html#using-rc-to-share-data","ch15-04-rc.html#cloning-an-rc-increases-the-reference-count","ch15-05-interior-mutability.html#refcell-and-the-interior-mutability-pattern","ch15-05-interior-mutability.html#enforcing-borrowing-rules-at-runtime-with-refcell","ch15-05-interior-mutability.html#interior-mutability-a-mutable-borrow-to-an-immutable-value","ch15-05-interior-mutability.html#having-multiple-owners-of-mutable-data-by-combining-rc-and-refcell","ch15-06-reference-cycles.html#reference-cycles-can-leak-memory","ch15-06-reference-cycles.html#creating-a-reference-cycle","ch15-06-reference-cycles.html#preventing-reference-cycles-turning-an-rc-into-a-weak","ch15-06-reference-cycles.html#summary","ch16-00-concurrency.html#fearless-concurrency","ch16-01-threads.html#using-threads-to-run-code-simultaneously","ch16-01-threads.html#creating-a-new-thread-with-spawn","ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles","ch16-01-threads.html#using-move-closures-with-threads","ch16-02-message-passing.html#using-message-passing-to-transfer-data-between-threads","ch16-02-message-passing.html#channels-and-ownership-transference","ch16-02-message-passing.html#sending-multiple-values-and-seeing-the-receiver-waiting","ch16-02-message-passing.html#creating-multiple-producers-by-cloning-the-transmitter","ch16-03-shared-state.html#shared-state-concurrency","ch16-03-shared-state.html#using-mutexes-to-allow-access-to-data-from-one-thread-at-a-time","ch16-03-shared-state.html#similarities-between-refcellrc-and-mutexarc","ch16-04-extensible-concurrency-sync-and-send.html#extensible-concurrency-with-the-sync-and-send-traits","ch16-04-extensible-concurrency-sync-and-send.html#allowing-transference-of-ownership-between-threads-with-send","ch16-04-extensible-concurrency-sync-and-send.html#allowing-access-from-multiple-threads-with-sync","ch16-04-extensible-concurrency-sync-and-send.html#implementing-send-and-sync-manually-is-unsafe","ch16-04-extensible-concurrency-sync-and-send.html#summary","ch17-00-async-await.html#async-and-await","ch17-00-async-await.html#parallelism-and-concurrency","ch17-01-futures-and-syntax.html#futures-and-the-async-syntax","ch17-01-futures-and-syntax.html#our-first-async-program","ch17-02-concurrency-with-async.html#concurrency-with-async","ch17-02-concurrency-with-async.html#counting","ch17-02-concurrency-with-async.html#message-passing","ch17-03-more-futures.html#working-with-any-number-of-futures","ch17-03-more-futures.html#racing-futures","ch17-03-more-futures.html#yielding","ch17-03-more-futures.html#building-our-own-async-abstractions","ch17-04-streams.html#streams","ch17-04-streams.html#composing-streams","ch17-04-streams.html#merging-streams","ch17-05-traits-for-async.html#digging-into-the-traits-for-async","ch17-05-traits-for-async.html#future","ch17-05-traits-for-async.html#pinning-and-the-pin-and-unpin-traits","ch17-05-traits-for-async.html#the-stream-trait","ch17-06-futures-tasks-threads.html#futures-tasks-and-threads","ch17-06-futures-tasks-threads.html#summary","ch18-00-oop.html#object-oriented-programming-features-of-rust","ch18-01-what-is-oo.html#characteristics-of-object-oriented-languages","ch18-01-what-is-oo.html#objects-contain-data-and-behavior","ch18-01-what-is-oo.html#encapsulation-that-hides-implementation-details","ch18-01-what-is-oo.html#inheritance-as-a-type-system-and-as-code-sharing","ch18-01-what-is-oo.html#polymorphism","ch18-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types","ch18-02-trait-objects.html#defining-a-trait-for-common-behavior","ch18-02-trait-objects.html#implementing-the-trait","ch18-02-trait-objects.html#trait-objects-perform-dynamic-dispatch","ch18-03-oo-design-patterns.html#implementing-an-object-oriented-design-pattern","ch18-03-oo-design-patterns.html#defining-post-and-creating-a-new-instance-in-the-draft-state","ch18-03-oo-design-patterns.html#storing-the-text-of-the-post-content","ch18-03-oo-design-patterns.html#ensuring-the-content-of-a-draft-post-is-empty","ch18-03-oo-design-patterns.html#requesting-a-review-of-the-post-changes-its-state","ch18-03-oo-design-patterns.html#adding-approve-to-change-the-behavior-of-content","ch18-03-oo-design-patterns.html#trade-offs-of-the-state-pattern","ch18-03-oo-design-patterns.html#summary","ch19-00-patterns.html#patterns-and-matching","ch19-01-all-the-places-for-patterns.html#all-the-places-patterns-can-be-used","ch19-01-all-the-places-for-patterns.html#match-arms","ch19-01-all-the-places-for-patterns.html#conditional-if-let-expressions","ch19-01-all-the-places-for-patterns.html#while-let-conditional-loops","ch19-01-all-the-places-for-patterns.html#for-loops","ch19-01-all-the-places-for-patterns.html#let-statements","ch19-01-all-the-places-for-patterns.html#function-parameters","ch19-02-refutability.html#refutability-whether-a-pattern-might-fail-to-match","ch19-03-pattern-syntax.html#pattern-syntax","ch19-03-pattern-syntax.html#matching-literals","ch19-03-pattern-syntax.html#matching-named-variables","ch19-03-pattern-syntax.html#multiple-patterns","ch19-03-pattern-syntax.html#matching-ranges-of-values-with-","ch19-03-pattern-syntax.html#destructuring-to-break-apart-values","ch19-03-pattern-syntax.html#ignoring-values-in-a-pattern","ch19-03-pattern-syntax.html#extra-conditionals-with-match-guards","ch19-03-pattern-syntax.html#-bindings","ch19-03-pattern-syntax.html#summary","ch20-00-advanced-features.html#advanced-features","ch20-01-unsafe-rust.html#unsafe-rust","ch20-01-unsafe-rust.html#unsafe-superpowers","ch20-01-unsafe-rust.html#dereferencing-a-raw-pointer","ch20-01-unsafe-rust.html#calling-an-unsafe-function-or-method","ch20-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable","ch20-01-unsafe-rust.html#implementing-an-unsafe-trait","ch20-01-unsafe-rust.html#accessing-fields-of-a-union","ch20-01-unsafe-rust.html#when-to-use-unsafe-code","ch20-03-advanced-traits.html#advanced-traits","ch20-03-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types","ch20-03-advanced-traits.html#default-generic-type-parameters-and-operator-overloading","ch20-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name","ch20-03-advanced-traits.html#using-supertraits-to-require-one-traits-functionality-within-another-trait","ch20-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types","ch20-04-advanced-types.html#advanced-types","ch20-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction","ch20-04-advanced-types.html#creating-type-synonyms-with-type-aliases","ch20-04-advanced-types.html#the-never-type-that-never-returns","ch20-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait","ch20-05-advanced-functions-and-closures.html#advanced-functions-and-closures","ch20-05-advanced-functions-and-closures.html#function-pointers","ch20-05-advanced-functions-and-closures.html#returning-closures","ch20-06-macros.html#macros","ch20-06-macros.html#the-difference-between-macros-and-functions","ch20-06-macros.html#declarative-macros-with-macro_rules-for-general-metaprogramming","ch20-06-macros.html#procedural-macros-for-generating-code-from-attributes","ch20-06-macros.html#how-to-write-a-custom-derive-macro","ch20-06-macros.html#attribute-like-macros","ch20-06-macros.html#function-like-macros","ch20-06-macros.html#summary","ch21-00-final-project-a-web-server.html#final-project-building-a-multithreaded-web-server","ch21-01-single-threaded.html#building-a-single-threaded-web-server","ch21-01-single-threaded.html#listening-to-the-tcp-connection","ch21-01-single-threaded.html#reading-the-request","ch21-01-single-threaded.html#a-closer-look-at-an-http-request","ch21-01-single-threaded.html#writing-a-response","ch21-01-single-threaded.html#returning-real-html","ch21-01-single-threaded.html#validating-the-request-and-selectively-responding","ch21-01-single-threaded.html#a-touch-of-refactoring","ch21-02-multithreaded.html#turning-our-single-threaded-server-into-a-multithreaded-server","ch21-02-multithreaded.html#simulating-a-slow-request-in-the-current-server-implementation","ch21-02-multithreaded.html#improving-throughput-with-a-thread-pool","ch21-03-graceful-shutdown-and-cleanup.html#graceful-shutdown-and-cleanup","ch21-03-graceful-shutdown-and-cleanup.html#implementing-the-drop-trait-on-threadpool","ch21-03-graceful-shutdown-and-cleanup.html#signaling-to-the-threads-to-stop-listening-for-jobs","ch21-03-graceful-shutdown-and-cleanup.html#summary","appendix-00.html#appendix","appendix-01-keywords.html#appendix-a-keywords","appendix-01-keywords.html#keywords-currently-in-use","appendix-01-keywords.html#keywords-reserved-for-future-use","appendix-01-keywords.html#raw-identifiers","appendix-02-operators.html#appendix-b-operators-and-symbols","appendix-02-operators.html#operators","appendix-02-operators.html#non-operator-symbols","appendix-03-derivable-traits.html#appendix-c-derivable-traits","appendix-03-derivable-traits.html#debug-for-programmer-output","appendix-03-derivable-traits.html#partialeq-and-eq-for-equality-comparisons","appendix-03-derivable-traits.html#partialord-and-ord-for-ordering-comparisons","appendix-03-derivable-traits.html#clone-and-copy-for-duplicating-values","appendix-03-derivable-traits.html#hash-for-mapping-a-value-to-a-value-of-fixed-size","appendix-03-derivable-traits.html#default-for-default-values","appendix-04-useful-development-tools.html#appendix-d---useful-development-tools","appendix-04-useful-development-tools.html#automatic-formatting-with-rustfmt","appendix-04-useful-development-tools.html#fix-your-code-with-rustfix","appendix-04-useful-development-tools.html#more-lints-with-clippy","appendix-04-useful-development-tools.html#ide-integration-using-rust-analyzer","appendix-05-editions.html#appendix-e---editions","appendix-06-translation.html#appendix-f-translations-of-the-book","appendix-07-nightly-rust.html#appendix-g---how-rust-is-made-and-nightly-rust","appendix-07-nightly-rust.html#stability-without-stagnation","appendix-07-nightly-rust.html#choo-choo-release-channels-and-riding-the-trains","appendix-07-nightly-rust.html#maintenance-time","appendix-07-nightly-rust.html#unstable-features","appendix-07-nightly-rust.html#rustup-and-the-role-of-rust-nightly","appendix-07-nightly-rust.html#the-rfc-process-and-teams"],"index":{"documentStore":{"docInfo":{"0":{"body":72,"breadcrumbs":6,"title":3},"1":{"body":226,"breadcrumbs":2,"title":1},"10":{"body":503,"breadcrumbs":3,"title":2},"100":{"body":675,"breadcrumbs":7,"title":2},"101":{"body":651,"breadcrumbs":11,"title":6},"102":{"body":337,"breadcrumbs":11,"title":4},"103":{"body":203,"breadcrumbs":10,"title":3},"104":{"body":265,"breadcrumbs":9,"title":2},"105":{"body":162,"breadcrumbs":9,"title":2},"106":{"body":313,"breadcrumbs":11,"title":4},"107":{"body":286,"breadcrumbs":9,"title":3},"108":{"body":82,"breadcrumbs":7,"title":1},"109":{"body":252,"breadcrumbs":12,"title":6},"11":{"body":6,"breadcrumbs":3,"title":2},"110":{"body":340,"breadcrumbs":10,"title":2},"111":{"body":29,"breadcrumbs":16,"title":5},"112":{"body":281,"breadcrumbs":14,"title":3},"113":{"body":346,"breadcrumbs":15,"title":4},"114":{"body":539,"breadcrumbs":16,"title":5},"115":{"body":502,"breadcrumbs":15,"title":4},"116":{"body":141,"breadcrumbs":15,"title":4},"117":{"body":315,"breadcrumbs":15,"title":4},"118":{"body":265,"breadcrumbs":16,"title":5},"119":{"body":249,"breadcrumbs":15,"title":4},"12":{"body":30,"breadcrumbs":4,"title":2},"120":{"body":88,"breadcrumbs":15,"title":4},"121":{"body":180,"breadcrumbs":16,"title":5},"122":{"body":161,"breadcrumbs":14,"title":3},"123":{"body":273,"breadcrumbs":19,"title":8},"124":{"body":68,"breadcrumbs":13,"title":2},"125":{"body":284,"breadcrumbs":14,"title":4},"126":{"body":148,"breadcrumbs":13,"title":3},"127":{"body":54,"breadcrumbs":11,"title":1},"128":{"body":123,"breadcrumbs":4,"title":2},"129":{"body":38,"breadcrumbs":10,"title":4},"13":{"body":77,"breadcrumbs":4,"title":1},"130":{"body":171,"breadcrumbs":9,"title":3},"131":{"body":54,"breadcrumbs":8,"title":2},"132":{"body":453,"breadcrumbs":9,"title":3},"133":{"body":150,"breadcrumbs":10,"title":4},"134":{"body":195,"breadcrumbs":11,"title":5},"135":{"body":58,"breadcrumbs":10,"title":4},"136":{"body":88,"breadcrumbs":14,"title":6},"137":{"body":90,"breadcrumbs":9,"title":1},"138":{"body":215,"breadcrumbs":11,"title":3},"139":{"body":476,"breadcrumbs":10,"title":2},"14":{"body":38,"breadcrumbs":6,"title":3},"140":{"body":501,"breadcrumbs":10,"title":2},"141":{"body":120,"breadcrumbs":10,"title":2},"142":{"body":88,"breadcrumbs":12,"title":4},"143":{"body":93,"breadcrumbs":10,"title":2},"144":{"body":105,"breadcrumbs":14,"title":6},"145":{"body":109,"breadcrumbs":12,"title":4},"146":{"body":108,"breadcrumbs":12,"title":4},"147":{"body":97,"breadcrumbs":11,"title":3},"148":{"body":469,"breadcrumbs":11,"title":3},"149":{"body":71,"breadcrumbs":10,"title":2},"15":{"body":94,"breadcrumbs":7,"title":4},"150":{"body":131,"breadcrumbs":9,"title":1},"151":{"body":130,"breadcrumbs":4,"title":2},"152":{"body":60,"breadcrumbs":8,"title":3},"153":{"body":626,"breadcrumbs":10,"title":5},"154":{"body":392,"breadcrumbs":8,"title":3},"155":{"body":497,"breadcrumbs":8,"title":3},"156":{"body":1435,"breadcrumbs":7,"title":2},"157":{"body":100,"breadcrumbs":6,"title":2},"158":{"body":81,"breadcrumbs":8,"title":4},"159":{"body":159,"breadcrumbs":8,"title":4},"16":{"body":43,"breadcrumbs":6,"title":3},"160":{"body":335,"breadcrumbs":7,"title":3},"161":{"body":467,"breadcrumbs":8,"title":4},"162":{"body":81,"breadcrumbs":5,"title":1},"163":{"body":153,"breadcrumbs":8,"title":4},"164":{"body":459,"breadcrumbs":8,"title":4},"165":{"body":30,"breadcrumbs":10,"title":3},"166":{"body":454,"breadcrumbs":9,"title":2},"167":{"body":322,"breadcrumbs":9,"title":2},"168":{"body":159,"breadcrumbs":9,"title":2},"169":{"body":422,"breadcrumbs":9,"title":2},"17":{"body":77,"breadcrumbs":4,"title":1},"170":{"body":180,"breadcrumbs":11,"title":4},"171":{"body":32,"breadcrumbs":12,"title":4},"172":{"body":194,"breadcrumbs":10,"title":2},"173":{"body":341,"breadcrumbs":11,"title":3},"174":{"body":399,"breadcrumbs":10,"title":2},"175":{"body":430,"breadcrumbs":10,"title":2},"176":{"body":302,"breadcrumbs":12,"title":4},"177":{"body":264,"breadcrumbs":14,"title":6},"178":{"body":92,"breadcrumbs":10,"title":3},"179":{"body":242,"breadcrumbs":11,"title":4},"18":{"body":28,"breadcrumbs":5,"title":2},"180":{"body":151,"breadcrumbs":9,"title":2},"181":{"body":298,"breadcrumbs":10,"title":3},"182":{"body":120,"breadcrumbs":10,"title":3},"183":{"body":620,"breadcrumbs":11,"title":4},"184":{"body":267,"breadcrumbs":10,"title":3},"185":{"body":129,"breadcrumbs":11,"title":4},"186":{"body":562,"breadcrumbs":9,"title":2},"187":{"body":209,"breadcrumbs":11,"title":4},"188":{"body":82,"breadcrumbs":9,"title":2},"189":{"body":101,"breadcrumbs":14,"title":7},"19":{"body":32,"breadcrumbs":5,"title":2},"190":{"body":102,"breadcrumbs":8,"title":1},"191":{"body":175,"breadcrumbs":6,"title":3},"192":{"body":46,"breadcrumbs":7,"title":2},"193":{"body":868,"breadcrumbs":8,"title":3},"194":{"body":640,"breadcrumbs":9,"title":4},"195":{"body":504,"breadcrumbs":10,"title":5},"196":{"body":355,"breadcrumbs":9,"title":4},"197":{"body":667,"breadcrumbs":8,"title":3},"198":{"body":136,"breadcrumbs":9,"title":4},"199":{"body":92,"breadcrumbs":9,"title":3},"2":{"body":66,"breadcrumbs":2,"title":1},"20":{"body":80,"breadcrumbs":6,"title":2},"200":{"body":158,"breadcrumbs":10,"title":4},"201":{"body":318,"breadcrumbs":9,"title":3},"202":{"body":353,"breadcrumbs":10,"title":4},"203":{"body":253,"breadcrumbs":11,"title":5},"204":{"body":69,"breadcrumbs":7,"title":2},"205":{"body":301,"breadcrumbs":7,"title":2},"206":{"body":892,"breadcrumbs":7,"title":2},"207":{"body":76,"breadcrumbs":6,"title":1},"208":{"body":215,"breadcrumbs":12,"title":6},"209":{"body":87,"breadcrumbs":14,"title":4},"21":{"body":63,"breadcrumbs":7,"title":3},"210":{"body":142,"breadcrumbs":13,"title":3},"211":{"body":191,"breadcrumbs":14,"title":4},"212":{"body":163,"breadcrumbs":14,"title":4},"213":{"body":281,"breadcrumbs":10,"title":2},"214":{"body":197,"breadcrumbs":16,"title":5},"215":{"body":527,"breadcrumbs":15,"title":4},"216":{"body":289,"breadcrumbs":15,"title":4},"217":{"body":794,"breadcrumbs":14,"title":3},"218":{"body":660,"breadcrumbs":14,"title":3},"219":{"body":263,"breadcrumbs":15,"title":4},"22":{"body":113,"breadcrumbs":8,"title":4},"220":{"body":126,"breadcrumbs":18,"title":6},"221":{"body":571,"breadcrumbs":15,"title":3},"222":{"body":856,"breadcrumbs":16,"title":4},"223":{"body":42,"breadcrumbs":12,"title":3},"224":{"body":269,"breadcrumbs":16,"title":7},"225":{"body":1186,"breadcrumbs":12,"title":3},"226":{"body":47,"breadcrumbs":22,"title":8},"227":{"body":146,"breadcrumbs":17,"title":3},"228":{"body":153,"breadcrumbs":18,"title":4},"229":{"body":59,"breadcrumbs":15,"title":1},"23":{"body":190,"breadcrumbs":7,"title":3},"230":{"body":102,"breadcrumbs":10,"title":5},"231":{"body":36,"breadcrumbs":15,"title":5},"232":{"body":387,"breadcrumbs":13,"title":3},"233":{"body":479,"breadcrumbs":14,"title":4},"234":{"body":515,"breadcrumbs":14,"title":4},"235":{"body":775,"breadcrumbs":17,"title":7},"236":{"body":211,"breadcrumbs":13,"title":4},"237":{"body":216,"breadcrumbs":13,"title":4},"238":{"body":117,"breadcrumbs":12,"title":3},"239":{"body":273,"breadcrumbs":12,"title":3},"24":{"body":204,"breadcrumbs":8,"title":4},"240":{"body":203,"breadcrumbs":13,"title":4},"241":{"body":25,"breadcrumbs":11,"title":3},"242":{"body":870,"breadcrumbs":12,"title":4},"243":{"body":364,"breadcrumbs":13,"title":5},"244":{"body":81,"breadcrumbs":12,"title":4},"245":{"body":377,"breadcrumbs":15,"title":5},"246":{"body":50,"breadcrumbs":11,"title":1},"247":{"body":51,"breadcrumbs":6,"title":3},"248":{"body":266,"breadcrumbs":11,"title":4},"249":{"body":40,"breadcrumbs":9,"title":3},"25":{"body":118,"breadcrumbs":6,"title":2},"250":{"body":538,"breadcrumbs":10,"title":4},"251":{"body":615,"breadcrumbs":12,"title":6},"252":{"body":69,"breadcrumbs":10,"title":4},"253":{"body":293,"breadcrumbs":10,"title":4},"254":{"body":107,"breadcrumbs":8,"title":2},"255":{"body":35,"breadcrumbs":11,"title":5},"256":{"body":124,"breadcrumbs":11,"title":5},"257":{"body":35,"breadcrumbs":7,"title":2},"258":{"body":196,"breadcrumbs":7,"title":2},"259":{"body":788,"breadcrumbs":9,"title":4},"26":{"body":326,"breadcrumbs":7,"title":3},"260":{"body":171,"breadcrumbs":12,"title":4},"261":{"body":41,"breadcrumbs":11,"title":4},"262":{"body":36,"breadcrumbs":8,"title":1},"263":{"body":286,"breadcrumbs":4,"title":2},"264":{"body":149,"breadcrumbs":12,"title":5},"265":{"body":119,"breadcrumbs":12,"title":5},"266":{"body":979,"breadcrumbs":11,"title":4},"267":{"body":103,"breadcrumbs":16,"title":7},"268":{"body":170,"breadcrumbs":12,"title":3},"269":{"body":90,"breadcrumbs":12,"title":3},"27":{"body":334,"breadcrumbs":8,"title":4},"270":{"body":216,"breadcrumbs":12,"title":3},"271":{"body":281,"breadcrumbs":15,"title":6},"272":{"body":374,"breadcrumbs":14,"title":5},"273":{"body":135,"breadcrumbs":13,"title":4},"274":{"body":341,"breadcrumbs":12,"title":5},"275":{"body":454,"breadcrumbs":11,"title":4},"276":{"body":159,"breadcrumbs":12,"title":5},"277":{"body":416,"breadcrumbs":11,"title":4},"278":{"body":277,"breadcrumbs":12,"title":5},"279":{"body":87,"breadcrumbs":10,"title":4},"28":{"body":62,"breadcrumbs":6,"title":2},"280":{"body":265,"breadcrumbs":11,"title":5},"281":{"body":1492,"breadcrumbs":12,"title":6},"282":{"body":294,"breadcrumbs":14,"title":8},"283":{"body":59,"breadcrumbs":10,"title":4},"284":{"body":585,"breadcrumbs":9,"title":3},"285":{"body":926,"breadcrumbs":12,"title":6},"286":{"body":100,"breadcrumbs":7,"title":1},"287":{"body":279,"breadcrumbs":4,"title":2},"288":{"body":169,"breadcrumbs":12,"title":5},"289":{"body":201,"breadcrumbs":11,"title":4},"29":{"body":73,"breadcrumbs":6,"title":2},"290":{"body":336,"breadcrumbs":13,"title":6},"291":{"body":582,"breadcrumbs":11,"title":4},"292":{"body":590,"breadcrumbs":16,"title":7},"293":{"body":228,"breadcrumbs":12,"title":3},"294":{"body":154,"breadcrumbs":15,"title":6},"295":{"body":158,"breadcrumbs":14,"title":5},"296":{"body":104,"breadcrumbs":8,"title":3},"297":{"body":1091,"breadcrumbs":13,"title":8},"298":{"body":119,"breadcrumbs":9,"title":4},"299":{"body":40,"breadcrumbs":12,"title":5},"3":{"body":11,"breadcrumbs":2,"title":1},"30":{"body":70,"breadcrumbs":5,"title":1},"300":{"body":96,"breadcrumbs":13,"title":6},"301":{"body":78,"breadcrumbs":12,"title":5},"302":{"body":65,"breadcrumbs":12,"title":5},"303":{"body":114,"breadcrumbs":8,"title":1},"304":{"body":342,"breadcrumbs":4,"title":2},"305":{"body":336,"breadcrumbs":4,"title":2},"306":{"body":225,"breadcrumbs":8,"title":3},"307":{"body":1625,"breadcrumbs":8,"title":3},"308":{"body":57,"breadcrumbs":6,"title":2},"309":{"body":694,"breadcrumbs":5,"title":1},"31":{"body":74,"breadcrumbs":6,"title":3},"310":{"body":1076,"breadcrumbs":6,"title":2},"311":{"body":1309,"breadcrumbs":8,"title":3},"312":{"body":282,"breadcrumbs":7,"title":2},"313":{"body":727,"breadcrumbs":6,"title":1},"314":{"body":450,"breadcrumbs":8,"title":3},"315":{"body":520,"breadcrumbs":4,"title":1},"316":{"body":686,"breadcrumbs":5,"title":2},"317":{"body":1054,"breadcrumbs":5,"title":2},"318":{"body":54,"breadcrumbs":8,"title":3},"319":{"body":350,"breadcrumbs":6,"title":1},"32":{"body":130,"breadcrumbs":7,"title":4},"320":{"body":1081,"breadcrumbs":9,"title":4},"321":{"body":369,"breadcrumbs":7,"title":2},"322":{"body":737,"breadcrumbs":8,"title":3},"323":{"body":74,"breadcrumbs":6,"title":1},"324":{"body":77,"breadcrumbs":10,"title":5},"325":{"body":43,"breadcrumbs":13,"title":4},"326":{"body":81,"breadcrumbs":13,"title":4},"327":{"body":351,"breadcrumbs":13,"title":4},"328":{"body":156,"breadcrumbs":14,"title":5},"329":{"body":118,"breadcrumbs":10,"title":1},"33":{"body":226,"breadcrumbs":5,"title":2},"330":{"body":212,"breadcrumbs":19,"title":7},"331":{"body":423,"breadcrumbs":16,"title":4},"332":{"body":533,"breadcrumbs":14,"title":2},"333":{"body":133,"breadcrumbs":17,"title":5},"334":{"body":368,"breadcrumbs":15,"title":5},"335":{"body":169,"breadcrumbs":17,"title":7},"336":{"body":131,"breadcrumbs":14,"title":4},"337":{"body":129,"breadcrumbs":15,"title":5},"338":{"body":314,"breadcrumbs":15,"title":5},"339":{"body":720,"breadcrumbs":15,"title":5},"34":{"body":210,"breadcrumbs":6,"title":3},"340":{"body":966,"breadcrumbs":14,"title":4},"341":{"body":77,"breadcrumbs":11,"title":1},"342":{"body":133,"breadcrumbs":4,"title":2},"343":{"body":16,"breadcrumbs":8,"title":3},"344":{"body":129,"breadcrumbs":7,"title":2},"345":{"body":240,"breadcrumbs":7,"title":2},"346":{"body":82,"breadcrumbs":7,"title":2},"347":{"body":101,"breadcrumbs":6,"title":1},"348":{"body":292,"breadcrumbs":6,"title":1},"349":{"body":135,"breadcrumbs":7,"title":2},"35":{"body":177,"breadcrumbs":6,"title":3},"350":{"body":439,"breadcrumbs":12,"title":5},"351":{"body":10,"breadcrumbs":6,"title":2},"352":{"body":42,"breadcrumbs":6,"title":2},"353":{"body":256,"breadcrumbs":7,"title":3},"354":{"body":47,"breadcrumbs":6,"title":2},"355":{"body":119,"breadcrumbs":7,"title":3},"356":{"body":783,"breadcrumbs":8,"title":4},"357":{"body":821,"breadcrumbs":7,"title":3},"358":{"body":445,"breadcrumbs":8,"title":4},"359":{"body":188,"breadcrumbs":5,"title":1},"36":{"body":282,"breadcrumbs":7,"title":4},"360":{"body":56,"breadcrumbs":5,"title":1},"361":{"body":111,"breadcrumbs":4,"title":2},"362":{"body":138,"breadcrumbs":6,"title":2},"363":{"body":206,"breadcrumbs":6,"title":2},"364":{"body":408,"breadcrumbs":7,"title":3},"365":{"body":1044,"breadcrumbs":8,"title":4},"366":{"body":241,"breadcrumbs":9,"title":5},"367":{"body":124,"breadcrumbs":7,"title":3},"368":{"body":44,"breadcrumbs":7,"title":3},"369":{"body":38,"breadcrumbs":7,"title":3},"37":{"body":109,"breadcrumbs":7,"title":4},"370":{"body":21,"breadcrumbs":6,"title":2},"371":{"body":339,"breadcrumbs":11,"title":7},"372":{"body":427,"breadcrumbs":10,"title":6},"373":{"body":847,"breadcrumbs":12,"title":8},"374":{"body":432,"breadcrumbs":13,"title":9},"375":{"body":251,"breadcrumbs":12,"title":8},"376":{"body":34,"breadcrumbs":6,"title":2},"377":{"body":141,"breadcrumbs":10,"title":6},"378":{"body":451,"breadcrumbs":9,"title":5},"379":{"body":338,"breadcrumbs":8,"title":4},"38":{"body":42,"breadcrumbs":6,"title":3},"380":{"body":377,"breadcrumbs":9,"title":5},"381":{"body":12,"breadcrumbs":8,"title":3},"382":{"body":382,"breadcrumbs":7,"title":2},"383":{"body":179,"breadcrumbs":7,"title":2},"384":{"body":63,"breadcrumbs":4,"title":1},"385":{"body":142,"breadcrumbs":7,"title":4},"386":{"body":464,"breadcrumbs":8,"title":5},"387":{"body":151,"breadcrumbs":8,"title":5},"388":{"body":1035,"breadcrumbs":7,"title":4},"389":{"body":103,"breadcrumbs":5,"title":2},"39":{"body":41,"breadcrumbs":6,"title":3},"390":{"body":96,"breadcrumbs":5,"title":2},"391":{"body":46,"breadcrumbs":4,"title":1},"392":{"body":144,"breadcrumbs":12,"title":6},"393":{"body":108,"breadcrumbs":16,"title":5},"394":{"body":468,"breadcrumbs":14,"title":3},"395":{"body":363,"breadcrumbs":13,"title":2},"396":{"body":173,"breadcrumbs":15,"title":4},"397":{"body":220,"breadcrumbs":13,"title":2},"398":{"body":233,"breadcrumbs":14,"title":3},"399":{"body":367,"breadcrumbs":15,"title":4},"4":{"body":109,"breadcrumbs":3,"title":2},"40":{"body":630,"breadcrumbs":7,"title":4},"400":{"body":213,"breadcrumbs":13,"title":2},"401":{"body":53,"breadcrumbs":18,"title":6},"402":{"body":214,"breadcrumbs":18,"title":6},"403":{"body":3568,"breadcrumbs":16,"title":4},"404":{"body":86,"breadcrumbs":12,"title":3},"405":{"body":815,"breadcrumbs":13,"title":4},"406":{"body":963,"breadcrumbs":14,"title":5},"407":{"body":31,"breadcrumbs":10,"title":1},"408":{"body":9,"breadcrumbs":2,"title":1},"409":{"body":38,"breadcrumbs":4,"title":2},"41":{"body":277,"breadcrumbs":6,"title":3},"410":{"body":213,"breadcrumbs":5,"title":3},"411":{"body":20,"breadcrumbs":6,"title":4},"412":{"body":173,"breadcrumbs":4,"title":2},"413":{"body":20,"breadcrumbs":8,"title":4},"414":{"body":314,"breadcrumbs":5,"title":1},"415":{"body":537,"breadcrumbs":7,"title":3},"416":{"body":168,"breadcrumbs":8,"title":4},"417":{"body":50,"breadcrumbs":7,"title":3},"418":{"body":103,"breadcrumbs":8,"title":4},"419":{"body":159,"breadcrumbs":8,"title":4},"42":{"body":859,"breadcrumbs":7,"title":4},"420":{"body":172,"breadcrumbs":8,"title":4},"421":{"body":49,"breadcrumbs":10,"title":6},"422":{"body":80,"breadcrumbs":7,"title":3},"423":{"body":20,"breadcrumbs":10,"title":5},"424":{"body":67,"breadcrumbs":8,"title":3},"425":{"body":149,"breadcrumbs":8,"title":3},"426":{"body":131,"breadcrumbs":8,"title":3},"427":{"body":59,"breadcrumbs":10,"title":5},"428":{"body":298,"breadcrumbs":6,"title":3},"429":{"body":27,"breadcrumbs":8,"title":4},"43":{"body":215,"breadcrumbs":7,"title":4},"430":{"body":6,"breadcrumbs":12,"title":6},"431":{"body":57,"breadcrumbs":9,"title":3},"432":{"body":326,"breadcrumbs":12,"title":6},"433":{"body":22,"breadcrumbs":8,"title":2},"434":{"body":110,"breadcrumbs":8,"title":2},"435":{"body":122,"breadcrumbs":10,"title":4},"436":{"body":132,"breadcrumbs":9,"title":3},"44":{"body":83,"breadcrumbs":6,"title":3},"45":{"body":370,"breadcrumbs":6,"title":3},"46":{"body":56,"breadcrumbs":4,"title":1},"47":{"body":87,"breadcrumbs":6,"title":3},"48":{"body":366,"breadcrumbs":7,"title":2},"49":{"body":206,"breadcrumbs":6,"title":1},"5":{"body":41,"breadcrumbs":2,"title":1},"50":{"body":312,"breadcrumbs":6,"title":1},"51":{"body":146,"breadcrumbs":7,"title":2},"52":{"body":769,"breadcrumbs":7,"title":2},"53":{"body":672,"breadcrumbs":7,"title":2},"54":{"body":164,"breadcrumbs":5,"title":1},"55":{"body":228,"breadcrumbs":5,"title":1},"56":{"body":330,"breadcrumbs":6,"title":2},"57":{"body":303,"breadcrumbs":7,"title":3},"58":{"body":121,"breadcrumbs":5,"title":1},"59":{"body":26,"breadcrumbs":7,"title":2},"6":{"body":38,"breadcrumbs":2,"title":1},"60":{"body":648,"breadcrumbs":6,"title":1},"61":{"body":848,"breadcrumbs":7,"title":2},"62":{"body":55,"breadcrumbs":6,"title":1},"63":{"body":38,"breadcrumbs":4,"title":2},"64":{"body":106,"breadcrumbs":4,"title":1},"65":{"body":349,"breadcrumbs":5,"title":2},"66":{"body":26,"breadcrumbs":5,"title":2},"67":{"body":142,"breadcrumbs":5,"title":2},"68":{"body":202,"breadcrumbs":5,"title":2},"69":{"body":1166,"breadcrumbs":5,"title":2},"7":{"body":16,"breadcrumbs":4,"title":3},"70":{"body":137,"breadcrumbs":5,"title":2},"71":{"body":224,"breadcrumbs":6,"title":3},"72":{"body":357,"breadcrumbs":6,"title":2},"73":{"body":531,"breadcrumbs":6,"title":2},"74":{"body":278,"breadcrumbs":6,"title":2},"75":{"body":23,"breadcrumbs":6,"title":2},"76":{"body":458,"breadcrumbs":6,"title":2},"77":{"body":729,"breadcrumbs":6,"title":2},"78":{"body":63,"breadcrumbs":5,"title":1},"79":{"body":64,"breadcrumbs":5,"title":1},"8":{"body":94,"breadcrumbs":5,"title":4},"80":{"body":81,"breadcrumbs":10,"title":5},"81":{"body":384,"breadcrumbs":11,"title":3},"82":{"body":106,"breadcrumbs":12,"title":4},"83":{"body":265,"breadcrumbs":14,"title":6},"84":{"body":138,"breadcrumbs":17,"title":9},"85":{"body":108,"breadcrumbs":12,"title":4},"86":{"body":191,"breadcrumbs":11,"title":3},"87":{"body":190,"breadcrumbs":13,"title":4},"88":{"body":108,"breadcrumbs":11,"title":2},"89":{"body":166,"breadcrumbs":14,"title":5},"9":{"body":42,"breadcrumbs":2,"title":1},"90":{"body":633,"breadcrumbs":14,"title":5},"91":{"body":43,"breadcrumbs":9,"title":2},"92":{"body":405,"breadcrumbs":9,"title":2},"93":{"body":145,"breadcrumbs":9,"title":2},"94":{"body":273,"breadcrumbs":10,"title":3},"95":{"body":145,"breadcrumbs":9,"title":2},"96":{"body":108,"breadcrumbs":10,"title":3},"97":{"body":49,"breadcrumbs":8,"title":1},"98":{"body":62,"breadcrumbs":6,"title":3},"99":{"body":154,"breadcrumbs":7,"title":2}},"docs":{"0":{"body":"by Steve Klabnik and Carol Nichols, with contributions from the Rust Community This version of the text assumes you’re using Rust 1.81.0 (released 2024-09-04) or later. See the “Installation” section of Chapter 1 to install or update Rust. The HTML format is available online at https://doc.rust-lang.org/stable/book/ and offline with installations of Rust made with rustup; run rustup doc --book to open. Several community translations are also available. This text is available in paperback and ebook format from No Starch Press . 🚨 Want a more interactive learning experience? Try out a different version of the Rust Book, featuring: quizzes, highlighting, visualizations, and more : https://rust-book.cs.brown.edu","breadcrumbs":"The Rust Programming Language » The Rust Programming Language","id":"0","title":"The Rust Programming Language"},"1":{"body":"It wasn’t always so clear, but the Rust programming language is fundamentally about empowerment : no matter what kind of code you are writing now, Rust empowers you to reach farther, to program with confidence in a wider variety of domains than you did before. Take, for example, “systems-level” work that deals with low-level details of memory management, data representation, and concurrency. Traditionally, this realm of programming is seen as arcane, accessible only to a select few who have devoted the necessary years learning to avoid its infamous pitfalls. And even those who practice it do so with caution, lest their code be open to exploits, crashes, or corruption. Rust breaks down these barriers by eliminating the old pitfalls and providing a friendly, polished set of tools to help you along the way. Programmers who need to “dip down” into lower-level control can do so with Rust, without taking on the customary risk of crashes or security holes, and without having to learn the fine points of a fickle toolchain. Better yet, the language is designed to guide you naturally towards reliable code that is efficient in terms of speed and memory usage. Programmers who are already working with low-level code can use Rust to raise their ambitions. For example, introducing parallelism in Rust is a relatively low-risk operation: the compiler will catch the classical mistakes for you. And you can tackle more aggressive optimizations in your code with the confidence that you won’t accidentally introduce crashes or vulnerabilities. But Rust isn’t limited to low-level systems programming. It’s expressive and ergonomic enough to make CLI apps, web servers, and many other kinds of code quite pleasant to write — you’ll find simple examples of both later in the book. Working with Rust allows you to build skills that transfer from one domain to another; you can learn Rust by writing a web app, then apply those same skills to target your Raspberry Pi. This book fully embraces the potential of Rust to empower its users. It’s a friendly and approachable text intended to help you level up not just your knowledge of Rust, but also your reach and confidence as a programmer in general. So dive in, get ready to learn—and welcome to the Rust community! — Nicholas Matsakis and Aaron Turon","breadcrumbs":"Foreword » Foreword","id":"1","title":"Foreword"},"10":{"body":"In general, this book assumes that you’re reading it in sequence from front to back. Later chapters build on concepts in earlier chapters, and earlier chapters might not delve into details on a particular topic but will revisit the topic in a later chapter. You’ll find two kinds of chapters in this book: concept chapters and project chapters. In concept chapters, you’ll learn about an aspect of Rust. In project chapters, we’ll build small programs together, applying what you’ve learned so far. Chapters 2, 12, and 20 are project chapters; the rest are concept chapters. Chapter 1 explains how to install Rust, how to write a “Hello, world!” program, and how to use Cargo, Rust’s package manager and build tool. Chapter 2 is a hands-on introduction to writing a program in Rust, having you build up a number guessing game. Here we cover concepts at a high level, and later chapters will provide additional detail. If you want to get your hands dirty right away, Chapter 2 is the place for that. Chapter 3 covers Rust features that are similar to those of other programming languages, and in Chapter 4 you’ll learn about Rust’s ownership system. If you’re a particularly meticulous learner who prefers to learn every detail before moving on to the next, you might want to skip Chapter 2 and go straight to Chapter 3, returning to Chapter 2 when you’d like to work on a project applying the details you’ve learned. Chapter 5 discusses structs and methods, and Chapter 6 covers enums, match expressions, and the if let control flow construct. You’ll use structs and enums to make custom types in Rust. In Chapter 7, you’ll learn about Rust’s module system and about privacy rules for organizing your code and its public Application Programming Interface (API). Chapter 8 discusses some common collection data structures that the standard library provides, such as vectors, strings, and hash maps. Chapter 9 explores Rust’s error-handling philosophy and techniques. Chapter 10 digs into generics, traits, and lifetimes, which give you the power to define code that applies to multiple types. Chapter 11 is all about testing, which even with Rust’s safety guarantees is necessary to ensure your program’s logic is correct. In Chapter 12, we’ll build our own implementation of a subset of functionality from the grep command line tool that searches for text within files. For this, we’ll use many of the concepts we discussed in the previous chapters. Chapter 13 explores closures and iterators: features of Rust that come from functional programming languages. In Chapter 14, we’ll examine Cargo in more depth and talk about best practices for sharing your libraries with others. Chapter 15 discusses smart pointers that the standard library provides and the traits that enable their functionality. In Chapter 16, we’ll walk through different models of concurrent programming and talk about how Rust helps you to program in multiple threads fearlessly. Chapter 17 looks at how Rust idioms compare to object-oriented programming principles you might be familiar with. Chapter 18 is a reference on patterns and pattern matching, which are powerful ways of expressing ideas throughout Rust programs. Chapter 19 contains a smorgasbord of advanced topics of interest, including unsafe Rust, macros, and more about lifetimes, traits, types, functions, and closures. In Chapter 20, we’ll complete a project in which we’ll implement a low-level multithreaded web server! Finally, some appendices contain useful information about the language in a more reference-like format. Appendix A covers Rust’s keywords, Appendix B covers Rust’s operators and symbols, Appendix C covers derivable traits provided by the standard library, Appendix D covers some useful development tools, and Appendix E explains Rust editions. In Appendix F, you can find translations of the book, and in Appendix G we’ll cover how Rust is made and what nightly Rust is. There is no wrong way to read this book: if you want to skip ahead, go for it! You might have to jump back to earlier chapters if you experience any confusion. But do whatever works for you. An important part of the process of learning Rust is learning how to read the error messages the compiler displays: these will guide you toward working code. As such, we’ll provide many examples that don’t compile along with the error message the compiler will show you in each situation. Know that if you enter and run a random example, it may not compile! Make sure you read the surrounding text to see whether the example you’re trying to run is meant to error. Ferris will also help you distinguish code that isn’t meant to work: Ferris Meaning This code does not compile! This code panics! This code does not produce the desired behavior. In most situations, we’ll lead you to the correct version of any code that doesn’t compile.","breadcrumbs":"Introduction » How to Use This Book","id":"10","title":"How to Use This Book"},"100":{"body":"We can create instances of each of the two variants of IpAddrKind like this: # enum IpAddrKind {\n# V4,\n# V6,\n# }\n# # fn main() { let four = IpAddrKind::V4; let six = IpAddrKind::V6;\n# # route(IpAddrKind::V4);\n# route(IpAddrKind::V6);\n# }\n# # fn route(ip_kind: IpAddrKind) {} Note that the variants of the enum are namespaced under its identifier, and we use a double colon to separate the two. This is useful because now both values IpAddrKind::V4 and IpAddrKind::V6 are of the same type: IpAddrKind. We can then, for instance, define a function that takes any IpAddrKind: # enum IpAddrKind {\n# V4,\n# V6,\n# }\n# # fn main() {\n# let four = IpAddrKind::V4;\n# let six = IpAddrKind::V6;\n# # route(IpAddrKind::V4);\n# route(IpAddrKind::V6);\n# }\n# fn route(ip_kind: IpAddrKind) {} And we can call this function with either variant: # enum IpAddrKind {\n# V4,\n# V6,\n# }\n# # fn main() {\n# let four = IpAddrKind::V4;\n# let six = IpAddrKind::V6;\n# route(IpAddrKind::V4); route(IpAddrKind::V6);\n# }\n# # fn route(ip_kind: IpAddrKind) {} Using enums has even more advantages. Thinking more about our IP address type, at the moment we don’t have a way to store the actual IP address data ; we only know what kind it is. Given that you just learned about structs in Chapter 5, you might be tempted to tackle this problem with structs as shown in Listing 6-1. # fn main() { enum IpAddrKind { V4, V6, } struct IpAddr { kind: IpAddrKind, address: String, } let home = IpAddr { kind: IpAddrKind::V4, address: String::from(\"127.0.0.1\"), }; let loopback = IpAddr { kind: IpAddrKind::V6, address: String::from(\"::1\"), };\n# } Listing 6-1: Storing the data and IpAddrKind variant of an IP address using a struct Here, we’ve defined a struct IpAddr that has two fields: a kind field that is of type IpAddrKind (the enum we defined previously) and an address field of type String. We have two instances of this struct. The first is home, and it has the value IpAddrKind::V4 as its kind with associated address data of 127.0.0.1. The second instance is loopback. It has the other variant of IpAddrKind as its kind value, V6, and has address ::1 associated with it. We’ve used a struct to bundle the kind and address values together, so now the variant is associated with the value. However, representing the same concept using just an enum is more concise: rather than an enum inside a struct, we can put data directly into each enum variant. This new definition of the IpAddr enum says that both V4 and V6 variants will have associated String values: # fn main() { enum IpAddr { V4(String), V6(String), } let home = IpAddr::V4(String::from(\"127.0.0.1\")); let loopback = IpAddr::V6(String::from(\"::1\"));\n# } We attach data to each variant of the enum directly, so there is no need for an extra struct. Here, it’s also easier to see another detail of how enums work: the name of each enum variant that we define also becomes a function that constructs an instance of the enum. That is, IpAddr::V4() is a function call that takes a String argument and returns an instance of the IpAddr type. We automatically get this constructor function defined as a result of defining the enum. There’s another advantage to using an enum rather than a struct: each variant can have different types and amounts of associated data. Version four IP addresses will always have four numeric components that will have values between 0 and 255. If we wanted to store V4 addresses as four u8 values but still express V6 addresses as one String value, we wouldn’t be able to with a struct. Enums handle this case with ease: # fn main() { enum IpAddr { V4(u8, u8, u8, u8), V6(String), } let home = IpAddr::V4(127, 0, 0, 1); let loopback = IpAddr::V6(String::from(\"::1\"));\n# } We’ve shown several different ways to define data structures to store version four and version six IP addresses. However, as it turns out, wanting to store IP addresses and encode which kind they are is so common that the standard library has a definition we can use! Let’s look at how the standard library defines IpAddr: it has the exact enum and variants that we’ve defined and used, but it embeds the address data inside the variants in the form of two different structs, which are defined differently for each variant: struct Ipv4Addr { // --snip--\n} struct Ipv6Addr { // --snip--\n} enum IpAddr { V4(Ipv4Addr), V6(Ipv6Addr),\n} This code illustrates that you can put any kind of data inside an enum variant: strings, numeric types, or structs, for example. You can even include another enum! Also, standard library types are often not much more complicated than what you might come up with. Note that even though the standard library contains a definition for IpAddr, we can still create and use our own definition without conflict because we haven’t brought the standard library’s definition into our scope. We’ll talk more about bringing types into scope in Chapter 7. Let’s look at another example of an enum in Listing 6-2: this one has a wide variety of types embedded in its variants. enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32),\n}\n# # fn main() {} Listing 6-2: A Message enum whose variants each store different amounts and types of values This enum has four variants with different types: Quit has no data associated with it at all. Move has named fields, like a struct does. Write includes a single String. ChangeColor includes three i32 values. Defining an enum with variants such as the ones in Listing 6-2 is similar to defining different kinds of struct definitions, except the enum doesn’t use the struct keyword and all the variants are grouped together under the Message type. The following structs could hold the same data that the preceding enum variants hold: struct QuitMessage; // unit struct\nstruct MoveMessage { x: i32, y: i32,\n}\nstruct WriteMessage(String); // tuple struct\nstruct ChangeColorMessage(i32, i32, i32); // tuple struct\n# # fn main() {} But if we used the different structs, each of which has its own type, we couldn’t as easily define a function to take any of these kinds of messages as we could with the Message enum defined in Listing 6-2, which is a single type. There is one more similarity between enums and structs: just as we’re able to define methods on structs using impl, we’re also able to define methods on enums. Here’s a method named call that we could define on our Message enum: # fn main() {\n# enum Message {\n# Quit,\n# Move { x: i32, y: i32 },\n# Write(String),\n# ChangeColor(i32, i32, i32),\n# }\n# impl Message { fn call(&self) { // method body would be defined here } } let m = Message::Write(String::from(\"hello\")); m.call();\n# } The body of the method would use self to get the value that we called the method on. In this example, we’ve created a variable m that has the value Message::Write(String::from(\"hello\")), and that is what self will be in the body of the call method when m.call() runs. Let’s look at another enum in the standard library that is very common and useful: Option.","breadcrumbs":"Enums and Pattern Matching » Defining an Enum » Enum Values","id":"100","title":"Enum Values"},"101":{"body":"This section explores a case study of Option, which is another enum defined by the standard library. The Option type encodes the very common scenario in which a value could be something or it could be nothing. For example, if you request the first item in a non-empty list, you would get a value. If you request the first item in an empty list, you would get nothing. Expressing this concept in terms of the type system means the compiler can check whether you’ve handled all the cases you should be handling; this functionality can prevent bugs that are extremely common in other programming languages. Programming language design is often thought of in terms of which features you include, but the features you exclude are important too. Rust doesn’t have the null feature that many other languages have. Null is a value that means there is no value there. In languages with null, variables can always be in one of two states: null or not-null. In his 2009 presentation “Null References: The Billion Dollar Mistake,” Tony Hoare, the inventor of null, has this to say: I call it my billion-dollar mistake. At that time, I was designing the first comprehensive type system for references in an object-oriented language. My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years. The problem with null values is that if you try to use a null value as a not-null value, you’ll get an error of some kind. Because this null or not-null property is pervasive, it’s extremely easy to make this kind of error. However, the concept that null is trying to express is still a useful one: a null is a value that is currently invalid or absent for some reason. The problem isn’t really with the concept but with the particular implementation. As such, Rust does not have nulls, but it does have an enum that can encode the concept of a value being present or absent. This enum is Option, and it is defined by the standard library as follows: enum Option { None, Some(T),\n} The Option enum is so useful that it’s even included in the prelude; you don’t need to bring it into scope explicitly. Its variants are also included in the prelude: you can use Some and None directly without the Option:: prefix. The Option enum is still just a regular enum, and Some(T) and None are still variants of type Option. The syntax is a feature of Rust we haven’t talked about yet. It’s a generic type parameter, and we’ll cover generics in more detail in Chapter 10. For now, all you need to know is that means that the Some variant of the Option enum can hold one piece of data of any type, and that each concrete type that gets used in place of T makes the overall Option type a different type. Here are some examples of using Option values to hold number types and string types: # fn main() { let some_number = Some(5); let some_char = Some('e'); let absent_number: Option = None;\n# } The type of some_number is Option. The type of some_char is Option, which is a different type. Rust can infer these types because we’ve specified a value inside the Some variant. For absent_number, Rust requires us to annotate the overall Option type: the compiler can’t infer the type that the corresponding Some variant will hold by looking only at a None value. Here, we tell Rust that we mean for absent_number to be of type Option. When we have a Some value, we know that a value is present and the value is held within the Some. When we have a None value, in some sense it means the same thing as null: we don’t have a valid value. So why is having Option any better than having null? In short, because Option and T (where T can be any type) are different types, the compiler won’t let us use an Option value as if it were definitely a valid value. For example, this code won’t compile, because it’s trying to add an i8 to an Option: # fn main() { let x: i8 = 5; let y: Option = Some(5); let sum = x + y;\n# } If we run this code, we get an error message like this one: $ cargo run Compiling enums v0.1.0 (file:///projects/enums)\nerror[E0277]: cannot add `Option` to `i8` --> src/main.rs:5:17 |\n5 | let sum = x + y; | ^ no implementation for `i8 + Option` | = help: the trait `Add