diff --git a/listings/ch15-smart-pointers/listing-15-03/src/main.rs b/listings/ch15-smart-pointers/listing-15-03/src/main.rs index a96f3d7b18..991f690985 100644 --- a/listings/ch15-smart-pointers/listing-15-03/src/main.rs +++ b/listings/ch15-smart-pointers/listing-15-03/src/main.rs @@ -4,6 +4,8 @@ enum List { } // ANCHOR: here +// --snip-- + use crate::List::{Cons, Nil}; fn main() { diff --git a/listings/ch15-smart-pointers/listing-15-19/src/main.rs b/listings/ch15-smart-pointers/listing-15-19/src/main.rs index 1bd7bc533a..06a25ebb40 100644 --- a/listings/ch15-smart-pointers/listing-15-19/src/main.rs +++ b/listings/ch15-smart-pointers/listing-15-19/src/main.rs @@ -7,6 +7,8 @@ use crate::List::{Cons, Nil}; use std::rc::Rc; // ANCHOR: here +// --snip-- + fn main() { let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil))))); println!("count after creating a = {}", Rc::strong_count(&a)); diff --git a/nostarch/chapter15.md b/nostarch/chapter15.md index c7a5099a59..4403925cb2 100644 --- a/nostarch/chapter15.md +++ b/nostarch/chapter15.md @@ -13,7 +13,7 @@ memory. This address refers to, or “points at,” some other data. The most common kind of pointer in Rust is a reference, which you learned about in Chapter 4. References are indicated by the `&` symbol and borrow the value they point to. They don’t have any special capabilities other than referring to -data, and have no overhead. +data, and they have no overhead. *Smart pointers*, on the other hand, are data structures that act like a pointer but also have additional metadata and capabilities. The concept of @@ -27,14 +27,7 @@ the number of owners and, when no owners remain, cleaning up the data. Rust, with its concept of ownership and borrowing, has an additional difference between references and smart pointers: while references only borrow data, in -many cases, smart pointers *own* the data they point to. - -Though we didn’t call them as such at the time, we’ve already encountered a few -smart pointers in this book, including `String` and `Vec` in Chapter 8. Both -these types count as smart pointers because they own some memory and allow you -to manipulate it. They also have metadata and extra capabilities or guarantees. -`String`, for example, stores its capacity as metadata and has the extra -ability to ensure its data will always be valid UTF-8. +many cases smart pointers *own* the data they point to. Smart pointers are usually implemented using structs. Unlike an ordinary struct, smart pointers implement the `Deref` and `Drop` traits. The `Deref` @@ -49,23 +42,23 @@ frequently in Rust, this chapter won’t cover every existing smart pointer. Man libraries have their own smart pointers, and you can even write your own. We’ll cover the most common smart pointers in the standard library: -* `Box` for allocating values on the heap +* `Box`, for allocating values on the heap * `Rc`, a reference counting type that enables multiple ownership * `Ref` and `RefMut`, accessed through `RefCell`, a type that enforces the borrowing rules at runtime instead of compile time In addition, we’ll cover the *interior mutability* pattern where an immutable type exposes an API for mutating an interior value. We’ll also discuss -*reference cycles*: how they can leak memory and how to prevent them. +reference cycles: how they can leak memory and how to prevent them. Let’s dive in! ## Using Box to Point to Data on the Heap -The most straightforward smart pointer is a *box*, whose type is written -`Box`. Boxes allow you to store data on the heap rather than the stack. What -remains on the stack is the pointer to the heap data. Refer to Chapter 4 to -review the difference between the stack and the heap. +The most straightforward smart pointer is a box, whose type is written +`Box`. *Boxes* allow you to store data on the heap rather than the stack. +What remains on the stack is the pointer to the heap data. Refer to Chapter 4 +to review the difference between the stack and the heap. Boxes don’t have performance overhead, other than storing their data on the heap instead of on the stack. But they don’t have many extra capabilities @@ -78,23 +71,23 @@ either. You’ll use them most often in these situations: * When you want to own a value and you care only that it’s a type that implements a particular trait rather than being of a specific type -We’ll demonstrate the first situation in the “Enabling Recursive Types with -Boxes” section. In the -second case, transferring ownership of a large amount of data can take a long -time because the data is copied around on the stack. To 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 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 18! +We’ll demonstrate the first situation in “Enabling Recursive Types with +Boxes”. In the second +case, transferring ownership of a large amount of data can take a long time +because the data is copied around on the stack. To 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 “Using Trait Objects That Allow for Values of Different +Types,” in Chapter 18 is devoted to that topic. +So what you learn here you’ll apply again in that section! -### Using a Box to Store Data on the Heap +### Using Box to Store Data on the Heap Before we discuss the heap storage use case for `Box`, we’ll cover the syntax and how to interact with values stored within a `Box`. -Listing 15-1 shows how to use a box to store an `i32` value on the heap: +Listing 15-1 shows how to use a box to store an `i32` value on the heap. src/main.rs @@ -119,16 +112,16 @@ Putting a single value on the heap isn’t very useful, so you won’t use boxes themselves in this way very often. Having values like a single `i32` on the stack, where they’re stored by default, is more appropriate in the majority of situations. Let’s look at a case where boxes allow us to define types that we -wouldn’t be allowed to if we didn’t have boxes. +wouldn’t be allowed to define if we didn’t have boxes. ### Enabling Recursive Types with Boxes -A value of *recursive type* can have another value of the same type as part of -itself. Recursive types pose an issue because, at compile time, Rust needs to -know how much space a type takes up. However, the nesting of values of -recursive types could theoretically continue infinitely, so Rust can’t know how -much space the value needs. Because boxes have a known size, we can enable -recursive types by inserting a box in the recursive type definition. +A value of a *recursive type* can have another value of the same type as part of +itself. Recursive types pose an issue because Rust needs to know at compile time +how much space a type takes up. However, the nesting of values of recursive +types could theoretically continue infinitely, so Rust can’t know how much space +the value needs. Because boxes have a known size, we can enable recursive types +by inserting a box in the recursive type definition. As an example of a recursive type, let’s explore the *cons list*. This is a data type commonly found in functional programming languages. The cons list type @@ -139,14 +132,14 @@ more complex situations involving recursive types. #### More Information About the Cons List A *cons list* is a data structure that comes from the Lisp programming language -and its dialects and is made up of nested pairs, and is the Lisp version of a -linked list. Its name comes from the `cons` function (short for “construct -function”) in Lisp that constructs a new pair from its two arguments. By +and its dialects, is made up of nested pairs, and is the Lisp version of a +linked list. Its name comes from the `cons` function (short for *construct +function*) in Lisp that constructs a new pair from its two arguments. By calling `cons` on a pair consisting of a value and another pair, we can construct cons lists made up of recursive pairs. For example, here’s a pseudocode representation of a cons list containing the -list 1, 2, 3 with each pair in parentheses: +list `1, 2, 3` with each pair in parentheses: ``` (1, (2, (3, Nil))) @@ -186,11 +179,13 @@ Listing 15-2: The first attempt at defining an enum to represent a cons list dat > any type. Using the `List` type to store the list `1, 2, 3` would look like the code in -Listing 15-3: +Listing 15-3. src/main.rs ``` +// --snip-- + use crate::List::{Cons, Nil}; fn main() { @@ -206,9 +201,8 @@ is one more `Cons` value that holds `3` and a `List` value, which is finally `Nil`, the non-recursive variant that signals the end of the list. If we try to compile the code in Listing 15-3, we get the error shown in -Listing 15-4: +Listing 15-4. -output.txt ``` $ cargo run @@ -246,7 +240,7 @@ Listing 15-4: The error we get when attempting to define a recursive enum The error shows this type “has infinite size.” The reason is that we’ve defined `List` with a variant that is recursive: it holds another value of itself directly. As a result, Rust can’t figure out how much space it needs to store a -`List` value. Let’s break down why we get this error. First, we’ll look at how +`List` value. Let’s break down why we get this error. First we’ll look at how Rust decides how much space it needs to store a value of a non-recursive type. #### Computing the Size of a Non-Recursive Type @@ -279,7 +273,7 @@ type needs, the compiler looks at the variants, starting with the `Cons` variant. The `Cons` variant holds a value of type `i32` and a value of type `List`, and this process continues infinitely, as shown in Figure 15-1. -An infinite Cons list +An infinite Cons list: a rectangle labeled 'Cons' split into two smaller rectangles. The first smaller rectangle holds the label 'i32', and the second smaller rectangle holds the label 'Cons' and a smaller version of the outer 'Cons' rectangle. The 'Cons' rectangles continue to hold smaller and smaller versions of themselves until the smallest comfortably-sized rectangle holds an infinity symbol, indicating that this repetition goes on forever Figure 15-1: An infinite `List` consisting of infinite `Cons` variants @@ -300,7 +294,7 @@ help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle | ++++ + ``` -In this suggestion, “indirection” means that instead of storing a value +In this suggestion, *indirection* means that instead of storing a value directly, we should change the data structure to store the value indirectly by storing a pointer to the value instead. @@ -314,7 +308,7 @@ this implementation is now more like placing the items next to one another rather than inside one another. We can change the definition of the `List` enum in Listing 15-2 and the usage -of the `List` in Listing 15-3 to the code in Listing 15-5, which will compile: +of the `List` in Listing 15-3 to the code in Listing 15-5, which will compile. src/main.rs @@ -335,13 +329,13 @@ Listing 15-5: Definition of `List` that uses `Box` in order to have a known s The `Cons` variant needs the size of an `i32` plus the space to store the box’s pointer data. The `Nil` variant stores no values, so it needs less space -than the `Cons` variant. We now know that any `List` value will take up the -size of an `i32` plus the size of a box’s pointer data. By using a box, we’ve -broken the infinite, recursive chain, so the compiler can figure out the size -it needs to store a `List` value. Figure 15-2 shows what the `Cons` variant -looks like now. +on the stack than the `Cons` variant. We now know that any `List` value will +take up the size of an `i32` plus the size of a box’s pointer data. By using a +box, we’ve broken the infinite, recursive chain, so the compiler can figure out +the size it needs to store a `List` value. Figure 15-2 shows what the `Cons` +variant looks like now. -A finite Cons list +A rectangle labeled 'Cons' split into two smaller rectangles. The first smaller rectangle holds the label 'i32', and the second smaller rectangle holds the label 'Box' with one inner rectangle that contains the label 'usize', representing the finite size of the box's pointer Figure 15-2: A `List` that is not infinitely sized because `Cons` holds a `Box` @@ -351,7 +345,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 18, too. +in Chapter 18. The `Box` type is a smart pointer because it implements the `Deref` trait, which allows `Box` values to be treated like references. When a `Box` @@ -361,7 +355,11 @@ even more important to the functionality provided by the other smart pointer types we’ll discuss in the rest of this chapter. Let’s explore these two traits in more detail. -## Treating Smart Pointers Like Regular References with the Deref Trait +## Treating Smart Pointers Like Regular References with Deref + + + + Implementing the `Deref` trait allows you to customize the behavior of the *dereference operator* `*` (not to be confused with the multiplication or glob @@ -377,21 +375,17 @@ smart pointers to work in ways similar to references. Then we’ll look at Rust’s *deref coercion* feature and how it lets us work with either references or smart pointers. -> Note: There’s one big difference between the `MyBox` type we’re about to -> build and the real `Box`: our version will not store its data on the heap. -> We are focusing this example on `Deref`, so where the data is actually stored -> is less important than the pointer-like behavior. - - + + -### Following the Pointer to the Value +### Following the Reference to the Value A regular reference is a type of pointer, and one way to think of a pointer is as an arrow to a value stored somewhere else. In Listing 15-6, we create a reference to an `i32` value and then use the dereference operator to follow the -reference to the value: +reference to the value. src/main.rs @@ -428,11 +422,6 @@ error[E0277]: can't compare `{integer}` with `&{integer}` | = help: the trait `PartialEq<&{integer}>` is not implemented for `{integer}` = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider dereferencing here - --> file:///home/.rustup/toolchains/1.82/lib/rustlib/src/rust/library/core/src/macros/mod.rs:46:35 - | -46| if !(*left_val == **right_val) { - | + For more information about this error, try `rustc --explain E0277`. error: could not compile `deref-example` (bin "deref-example") due to 1 previous error @@ -447,7 +436,7 @@ to the value it’s pointing to. We can rewrite the code in Listing 15-6 to use a `Box` instead of a reference; the dereference operator used on the `Box` in Listing 15-7 functions in the same way as the dereference operator used on the reference in -Listing 15-6: +Listing 15-6. src/main.rs @@ -464,20 +453,24 @@ fn main() { Listing 15-7: Using the dereference operator on a `Box` The main difference between Listing 15-7 and Listing 15-6 is that here we set -`y` to be an instance of a `Box` pointing to a copied value of `x` rather -than a reference pointing to the value of `x`. In the last assertion, we can -use the dereference operator to follow the pointer of the `Box` in the same -way that we did when `y` was a reference. Next, we’ll explore what is special -about `Box` that enables us to use the dereference operator by defining our -own type. +`y` to be an instance of a box pointing to a copied value of `x` rather than a +reference pointing to the value of `x`. In the last assertion, we can use the +dereference operator to follow the box’s pointer in the same way that we did +when `y` was a reference. Next, we’ll explore what is special about `Box` +that enables us to use the dereference operator by defining our own box type. ### Defining Our Own Smart Pointer -Let’s build a smart pointer similar to the `Box` type provided by the -standard library to experience how smart pointers behave differently from +Let’s build a wrapper type similar to the `Box` type provided by the +standard library to experience how smart pointer types behave differently from references by default. Then we’ll look at how to add the ability to use the dereference operator. +> Note: There’s one big difference between the `MyBox` type we’re about to +> build and the real `Box`: our version will not store its data on the heap. +> We are focusing this example on `Deref`, so where the data is actually stored +> is less important than the pointer-like behavior. + The `Box` type is ultimately defined as a tuple struct with one element, so Listing 15-8 defines a `MyBox` type in the same way. We’ll also define a `new` function to match the `new` function defined on `Box`. @@ -496,7 +489,7 @@ impl MyBox { Listing 15-8: Defining a `MyBox` type -We define a struct named `MyBox` and declare a generic parameter `T`, because +We define a struct named `MyBox` and declare a generic parameter `T` because we want our type to hold values of any type. The `MyBox` type is a tuple struct with one element of type `T`. The `MyBox::new` function takes one parameter of type `T` and returns a `MyBox` instance that holds the value passed in. @@ -520,7 +513,7 @@ fn main() { Listing 15-9: Attempting to use `MyBox` in the same way we used references and `Box` -Here’s the resulting compilation error: +Here’s the resultant compilation error: ``` $ cargo run @@ -546,11 +539,11 @@ implement the `Deref` trait. ### Implementing the Deref Trait As discussed in “Implementing a Trait on a Type” in -Chapter 10, to implement a trait, we need to provide implementations for the +Chapter 10, to implement a trait we need to provide implementations for the trait’s required methods. The `Deref` trait, provided by the standard library, requires us to implement one method named `deref` that borrows `self` and returns a reference to the inner data. Listing 15-10 contains an implementation -of `Deref` to add to the definition of `MyBox`: +of `Deref` to add to the definition of `MyBox`. src/main.rs @@ -574,11 +567,11 @@ generic parameter, but you don’t need to worry about them for now; we’ll cov them in more detail in Chapter 20. We fill in the body of the `deref` method with `&self.0` so `deref` returns a -reference to the value we want to access with the `*` operator; recall from the -“Using Tuple Structs without Named Fields to Create Different -Types” section of Chapter 5 that `.0` accesses -the first value in a tuple struct. The `main` function in Listing 15-9 that -calls `*` on the `MyBox` value now compiles, and the assertions pass! +reference to the value we want to access with the `*` operator; recall from +“Using Tuple Structs Without Named Fields to Create Different +Types” in Chapter 5 that `.0` accesses the first +value in a tuple struct. The `main` function in Listing 15-9 that calls `*` on +the `MyBox` value now compiles, and the assertions pass! Without the `Deref` trait, the compiler can only dereference `&` references. The `deref` method gives the compiler the ability to take a value of any type @@ -600,7 +593,7 @@ identically whether we have a regular reference or a type that implements The reason the `deref` method returns a reference to a value, and that the plain dereference outside the parentheses in `*(y.deref())` is still necessary, -is to do with the ownership system. If the `deref` method returned the value +has to do with the ownership system. If the `deref` method returned the value directly instead of a reference to the value, the value would be moved out of `self`. We don’t want to take ownership of the inner value inside `MyBox` in this case or in most cases where we use the dereference operator. @@ -631,7 +624,7 @@ can work for either references or smart pointers. To see deref coercion in action, let’s use the `MyBox` type we defined in Listing 15-8 as well as the implementation of `Deref` that we added in Listing 15-10. Listing 15-11 shows the definition of a function that has a string slice -parameter: +parameter. src/main.rs @@ -644,8 +637,8 @@ fn hello(name: &str) { Listing 15-11: A `hello` function that has the parameter `name` of type `&str` We can call the `hello` function with a string slice as an argument, such as -`hello("Rust");` for example. Deref coercion makes it possible to call `hello` -with a reference to a value of type `MyBox`, as shown in Listing 15-12: +`hello("Rust");`, for example. Deref coercion makes it possible to call `hello` +with a reference to a value of type `MyBox`, as shown in Listing 15-12. src/main.rs @@ -706,10 +699,10 @@ cases: 1. From `&mut T` to `&mut U` when `T: DerefMut` 1. From `&mut T` to `&U` when `T: Deref` -The first two cases are the same as each other except that the second -implements mutability. The first case states that if you have a `&T`, and `T` -implements `Deref` to some type `U`, you can get a `&U` transparently. The -second case states that the same deref coercion happens for mutable references. +The first two cases are the same except that the second implements mutability. +The first case states that if you have a `&T`, and `T` implements `Deref` to +some type `U`, you can get a `&U` transparently. The second case states that the +same deref coercion happens for mutable references. The third case is trickier: Rust will also coerce a mutable reference to an immutable one. But the reverse is *not* possible: immutable references will @@ -732,12 +725,12 @@ be used to release resources like files or network connections. We’re introducing `Drop` in the context of smart pointers because the functionality of the `Drop` trait is almost always used when implementing a -smart pointer. For example, when a `Box` is dropped, it will deallocate the +smart pointer. For example, when a `Box` is dropped it will deallocate the space on the heap that the box points to. In some languages, for some types, the programmer must call code to free memory or resources every time they finish using an instance of those types. Examples -include file handles, sockets, or locks. If they forget, the system might +include file handles, sockets, and locks. If they forget, the system might become overloaded and crash. In Rust, you can specify that a particular bit of code be run whenever a value goes out of scope, and the compiler will insert this code automatically. As a result, you don’t need to be careful about @@ -751,7 +744,7 @@ let’s implement `drop` with `println!` statements for now. Listing 15-14 shows a `CustomSmartPointer` struct whose only custom functionality is that it will print `Dropping CustomSmartPointer!` when the -instance goes out of scope, to show when Rust runs the `drop` function. +instance goes out of scope, to show when Rust runs the `drop` method. src/main.rs @@ -782,8 +775,8 @@ Listing 15-14: A `CustomSmartPointer` struct that implements the `Drop` trait wh The `Drop` trait is included in the prelude, so we don’t need to bring it into scope. We implement the `Drop` trait on `CustomSmartPointer` and provide an implementation for the `drop` method that calls `println!`. The body of the -`drop` function is where you would place any logic that you wanted to run when -an instance of your type goes out of scope. We’re printing some text here to +`drop` method is where you would place any logic that you wanted to run when an +instance of your type goes out of scope. We’re printing some text here to demonstrate visually when Rust will call `drop`. In `main`, we create two instances of `CustomSmartPointer` and then print @@ -811,7 +804,9 @@ give you a visual guide to how the `drop` method works; usually you would specify the cleanup code that your type needs to run rather than a print message. -### Dropping a Value Early with std::mem::drop + + + Unfortunately, it’s not straightforward to disable the automatic `drop` functionality. Disabling `drop` isn’t usually necessary; the whole point of the @@ -819,13 +814,13 @@ functionality. Disabling `drop` isn’t usually necessary; the whole point of th you might want to clean up a value early. One example is when using smart pointers that manage locks: you might want to force the `drop` method that releases the lock so that other code in the same scope can acquire the lock. -Rust doesn’t let you call the `Drop` trait’s `drop` method manually; instead +Rust doesn’t let you call the `Drop` trait’s `drop` method manually; instead, you have to call the `std::mem::drop` function provided by the standard library if you want to force a value to be dropped before the end of its scope. If we try to call the `Drop` trait’s `drop` method manually by modifying the `main` function from Listing 15-14, as shown in Listing 15-15, we’ll get a -compiler error: +compiler error. src/main.rs @@ -864,7 +859,7 @@ error: could not compile `drop-example` (bin "drop-example") due to 1 previous e This error message states that we’re not allowed to explicitly call `drop`. The error message uses the term *destructor*, which is the general programming term -for a function that cleans up an instance. A destructor is analogous to a +for a function that cleans up an instance. A *destructor* is analogous to a *constructor*, which creates an instance. The `drop` function in Rust is one particular destructor. @@ -878,9 +873,9 @@ scope, and we can’t call the `drop` method explicitly. So, if we need to force a value to be cleaned up early, we use the `std::mem::drop` function. The `std::mem::drop` function is different from the `drop` method in the `Drop` -trait. We call it by passing as an argument the value we want to force drop. +trait. We call it by passing as an argument the value we want to force-drop. The function is in the prelude, so we can modify `main` in Listing 15-15 to -call the `drop` function, as shown in Listing 15-16: +call the `drop` function, as shown in Listing 15-16. src/main.rs @@ -946,7 +941,7 @@ Imagine `Rc` as a TV in a family room. When one person enters to watch TV, they turn it on. Others can come into the room and watch the TV. When the last person leaves the room, they turn off the TV because it’s no longer being used. If someone turns off the TV while others are still watching it, there would be -uproar from the remaining TV watchers! +an uproar from the remaining TV watchers! We use the `Rc` type when we want to allocate some data on the heap for multiple parts of our program to read and we can’t determine at compile time @@ -962,20 +957,20 @@ multithreaded programs. Let’s return to our cons list example in Listing 15-5. Recall that we defined it using `Box`. This time, we’ll create two lists that both share ownership -of a third list. Conceptually, this looks similar to Figure 15-3: +of a third list. Conceptually, this looks similar to Figure 15-3. -Two lists that share ownership of a third list +A linked list with the label 'a' pointing to three elements: the first element contains the integer 5 and points to the second element. The second element contains the integer 10 and points to the third element. The third element contains the value 'Nil' that signifies the end of the list; it does not point anywhere. A linked list with the label 'b' points to an element that contains the integer 3 and points to the first element of list 'a'. A linked list with the label 'c' points to an element that contains the integer 4 and also points to the first element of list 'a', so that the tail of lists 'b' and 'c' are both list 'a' Figure 15-3: Two lists, `b` and `c`, sharing ownership of a third list, `a` -We’ll create list `a` that contains 5 and then 10. Then we’ll make two more -lists: `b` that starts with 3 and `c` that starts with 4. Both `b` and `c` -lists will then continue on to the first `a` list containing 5 and 10. In other -words, both lists will share the first list containing 5 and 10. +We’ll create list `a` that contains `5` and then `10`. Then we’ll make two more +lists: `b` that starts with `3` and `c` that starts with `4`. Both `b` and `c` +lists will then continue on to the first `a` list containing `5` and `10`. In +other words, both lists will share the first list containing `5` and `10`. Trying to implement this scenario using our definition of `List` with `Box` -won’t work, as shown in Listing 15-17: +won’t work, as shown in Listing 15-17. src/main.rs @@ -994,7 +989,7 @@ fn main() { } ``` -Listing 15-17: Demonstrating we’re not allowed to have two lists using `Box` that try to share ownership of a third list +Listing 15-17: Demonstrating that we’re not allowed to have two lists using `Box` that try to share ownership of a third list When we compile this code, we get this error: @@ -1057,8 +1052,8 @@ fn main() { Listing 15-18: A definition of `List` that uses `Rc` We need to add a `use` statement to bring `Rc` into scope because it’s not -in the prelude. In `main`, we create the list holding 5 and 10 and store it in -a new `Rc` in `a`. Then when we create `b` and `c`, we call the +in the prelude. In `main`, we create the list holding `5` and `10` and store it +in a new `Rc` in `a`. Then, when we create `b` and `c`, we call the `Rc::clone` function and pass a reference to the `Rc` in `a` as an argument. @@ -1084,6 +1079,8 @@ then we can see how the reference count changes when `c` goes out of scope. src/main.rs ``` +// --snip-- + fn main() { let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil))))); println!("count after creating a = {}", Rc::strong_count(&a)); @@ -1103,8 +1100,7 @@ At each point in the program where the reference count changes, we print the reference count, which we get by calling the `Rc::strong_count` function. This function is named `strong_count` rather than `count` because the `Rc` type also has a `weak_count`; we’ll see what `weak_count` is used for in “Preventing -Reference Cycles: Turning an `Rc` into a -`Weak`”. +Reference Cycles Using `Weak`”. This code prints the following: @@ -1162,11 +1158,11 @@ interior mutability pattern. ### Enforcing Borrowing Rules at Runtime with RefCell Unlike `Rc`, the `RefCell` type represents single ownership over the data -it holds. So, what makes `RefCell` different from a type like `Box`? +it holds. So what makes `RefCell` different from a type like `Box`? Recall the borrowing rules you learned in Chapter 4: -* At any given time, you can have *either* (but not both) one mutable reference - or any number of immutable references. +* At any given time, you can have *either* one mutable reference or any number + of immutable references (but not both). * References must always be valid. With references and `Box`, the borrowing rules’ invariants are enforced at @@ -1190,8 +1186,8 @@ beyond the scope of this book but is an interesting topic to research. Because some analysis is impossible, if the Rust compiler can’t be sure the code complies with the ownership rules, it might reject a correct program; in this way, it’s conservative. If Rust accepted an incorrect program, users -wouldn’t be able to trust in the guarantees Rust makes. However, if Rust -rejects a correct program, the programmer will be inconvenienced, but nothing +wouldn’t be able to trust the guarantees Rust makes. However, if Rust rejects a +correct program, the programmer will be inconvenienced, but nothing catastrophic can occur. The `RefCell` type is useful when you’re sure your code follows the borrowing rules but the compiler is unable to understand and guarantee that. @@ -1263,13 +1259,13 @@ an immutable value and see why that is useful. #### A Use Case for Interior Mutability: Mock Objects Sometimes during testing a programmer will use a type in place of another type, -in order to observe particular behavior and assert it’s implemented correctly. -This placeholder type is called a *test double*. Think of it in the sense of a -“stunt double” in filmmaking, where a person steps in and substitutes for an -actor to do a particular tricky scene. Test doubles stand in for other types -when we’re running tests. *Mock objects* are specific types of test doubles -that record what happens during a test so you can assert that the correct -actions took place. +in order to observe particular behavior and assert that it’s implemented +correctly. This placeholder type is called a *test double*. Think of it in the +sense of a stunt double in filmmaking, where a person steps in and substitutes +for an actor to do a particularly tricky scene. Test doubles stand in for other +types when we’re running tests. *Mock objects* are specific types of test +doubles that record what happens during a test so you can assert that the +correct actions took place. Rust doesn’t have objects in the same sense as other languages have objects, and Rust doesn’t have mock object functionality built into the standard library @@ -1284,10 +1280,10 @@ user’s quota for the number of API calls they’re allowed to make, for exampl Our library will only provide the functionality of tracking how close to the maximum a value is and what the messages should be at what times. Applications that use our library will be expected to provide the mechanism for sending the -messages: the application could put a message in the application, send an -email, send a text message, or something else. The library doesn’t need to know -that detail. All it needs is something that implements a trait we’ll provide -called `Messenger`. Listing 15-20 shows the library code: +messages: the application could put a message in the application, send an email, +send a text message, or do something else. The library doesn’t need to know that +detail. All it needs is something that implements a trait we’ll provide called +`Messenger`. Listing 15-20 shows the library code. src/lib.rs @@ -1343,14 +1339,14 @@ is that we want to test the behavior of the `set_value` method on the `set_value` doesn’t return anything for us to make assertions on. We want to be able to say that if we create a `LimitTracker` with something that implements the `Messenger` trait and a particular value for `max`, when we pass different -numbers for `value`, the messenger is told to send the appropriate messages. +numbers for `value` the messenger is told to send the appropriate messages. We need a mock object that, instead of sending an email or text message when we call `send`, will only keep track of the messages it’s told to send. We can create a new instance of the mock object, create a `LimitTracker` that uses the mock object, call the `set_value` method on `LimitTracker`, and then check that the mock object has the messages we expect. Listing 15-21 shows an attempt to -implement a mock object to do just that, but the borrow checker won’t allow it: +implement a mock object to do just that, but the borrow checker won’t allow it. src/lib.rs @@ -1401,13 +1397,13 @@ take the message passed in as a parameter and store it in the `MockMessenger` list of `sent_messages`. In the test, we’re testing what happens when the `LimitTracker` is told to set -`value` to something that is more than 75 percent of the `max` value. First, we +`value` to something that is more than 75 percent of the `max` value. First we create a new `MockMessenger`, which will start with an empty list of messages. Then we create a new `LimitTracker` and give it a reference to the new -`MockMessenger` and a `max` value of 100. We call the `set_value` method on the -`LimitTracker` with a value of 80, which is more than 75 percent of 100. Then -we assert that the list of messages that the `MockMessenger` is keeping track -of should now have one message in it. +`MockMessenger` and a `max` value of `100`. We call the `set_value` method on +the `LimitTracker` with a value of `80`, which is more than 75 percent of 100. +Then we assert that the list of messages that the `MockMessenger` is keeping +track of should now have one message in it. However, there’s one problem with this test, as shown here: @@ -1433,17 +1429,17 @@ For more information about this error, try `rustc --explain E0596`. error: could not compile `limit-tracker` (lib test) due to 1 previous error ``` -We can’t modify the `MockMessenger` to keep track of the messages, because the +We can’t modify the `MockMessenger` to keep track of the messages because the `send` method takes an immutable reference to `self`. We also can’t take the suggestion from the error text to use `&mut self` in both the `impl` method and -the `trait` definition. We do not want to change the `Messenger` trait solely -for the sake of testing. Instead, we need to find a way to make our test code -work correctly with our existing design. +the trait definition. We do not want to change the `Messenger` trait solely for +the sake of testing. Instead, we need to find a way to make our test code work +correctly with our existing design. This is a situation in which interior mutability can help! We’ll store the `sent_messages` within a `RefCell`, and then the `send` method will be able to modify `sent_messages` to store the messages we’ve seen. Listing 15-22 -shows what that looks like: +shows what that looks like. src/lib.rs @@ -1511,7 +1507,7 @@ can treat them like regular references. The `RefCell` keeps track of how many `Ref` and `RefMut` smart pointers are currently active. Every time we call `borrow`, the `RefCell` increases its count of how many immutable borrows are active. When a `Ref` -value goes out of scope, the count of immutable borrows goes down by one. Just +value goes out of scope, the count of immutable borrows goes down by 1. Just like the compile-time borrowing rules, `RefCell` lets us have many immutable borrows or one mutable borrow at any point in time. @@ -1556,6 +1552,7 @@ test tests::it_sends_an_over_75_percent_warning_message ... FAILED failures: ---- tests::it_sends_an_over_75_percent_warning_message stdout ---- + thread 'tests::it_sends_an_over_75_percent_warning_message' panicked at src/lib.rs:60:53: already borrowed: BorrowMutError note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace @@ -1583,20 +1580,23 @@ in a context where only immutable values are allowed. You can use `RefCell` despite its trade-offs to get more functionality than regular references provide. -### Having Multiple Owners of Mutable Data by Combining Rc and RefCell + + + + +### Allowing Multiple Owners of Mutable Data with Rc and RefCell A common way to use `RefCell` is in combination with `Rc`. Recall that `Rc` lets you have multiple owners of some data, but it only gives immutable access to that data. If you have an `Rc` that holds a `RefCell`, you can get a value that can have multiple owners *and* that you can mutate! -For example, recall the cons list example in Listing 15-18 where we used -`Rc` to allow multiple lists to share ownership of another list. Because -`Rc` holds only immutable values, we can’t change any of the values in the -list once we’ve created them. Let’s add in `RefCell` to gain the ability to -change the values in the lists. Listing 15-24 shows that by using a -`RefCell` in the `Cons` definition, we can modify the value stored in all -the lists: +For example, recall the cons list example in Listing 15-18 where we used `Rc` +to allow multiple lists to share ownership of another list. Because `Rc` +holds only immutable values, we can’t change any of the values in the list once +we’ve created them. Let’s add in `RefCell` for its ability to change the +values in the lists. Listing 15-24 shows that by using a `RefCell` in the +`Cons` definition, we can modify the value stored in all the lists. src/main.rs @@ -1641,14 +1641,14 @@ they can both refer to `a`, which is what we did in Listing 15-18. After we’ve created the lists in `a`, `b`, and `c`, we want to add 10 to the value in `value`. We do this by calling `borrow_mut` on `value`, which uses the -automatic dereferencing feature we discussed in Chapter 5 (see “Where’s the -`->` Operator?”) to dereference the -`Rc` to the inner `RefCell` value. The `borrow_mut` method returns a +automatic dereferencing feature we discussed in “Where’s the `->` +Operator?”) in Chapter 5 to dereference +the `Rc` to the inner `RefCell` value. The `borrow_mut` method returns a `RefMut` smart pointer, and we use the dereference operator on it and change the inner value. When we print `a`, `b`, and `c`, we can see that they all have the modified -value of 15 rather than 5: +value of `15` rather than `5`: ``` $ cargo run @@ -1666,7 +1666,7 @@ access to its interior mutability so we can modify our data when we need to. The runtime checks of the borrowing rules protect us from data races, and it’s sometimes worth trading a bit of speed for this flexibility in our data structures. Note that `RefCell` does not work for multithreaded code! -`Mutex` is the thread-safe version of `RefCell` and we’ll discuss +`Mutex` is the thread-safe version of `RefCell`, and we’ll discuss `Mutex` in Chapter 16. ## Reference Cycles Can Leak Memory @@ -1684,7 +1684,7 @@ will never be dropped. Let’s look at how a reference cycle might happen and how to prevent it, starting with the definition of the `List` enum and a `tail` method in Listing -15-25: +15-25. src/main.rs @@ -1758,7 +1758,7 @@ Listing 15-26: Creating a reference cycle of two `List` values pointing to each We create an `Rc` instance holding a `List` value in the variable `a` with an initial list of `5, Nil`. We then create an `Rc` instance holding -another `List` value in the variable `b` that contains the value 10 and points +another `List` value in the variable `b` that contains the value `10` and points to the list in `a`. We modify `a` so it points to `b` instead of `Nil`, creating a cycle. We do @@ -1784,25 +1784,25 @@ b rc count after changing a = 2 a rc count after changing a = 2 ``` -The reference count of the `Rc` instances in both `a` and `b` are 2 after +The reference count of the `Rc` instances in both `a` and `b` is 2 after we change the list in `a` to point to `b`. At the end of `main`, Rust drops the variable `b`, which decreases the reference count of the `b` `Rc` instance from 2 to 1. The memory that `Rc` has on the heap won’t be dropped at -this point, because its reference count is 1, not 0. Then Rust drops `a`, which +this point because its reference count is 1, not 0. Then Rust drops `a`, which decreases the reference count of the `a` `Rc` instance from 2 to 1 as well. This instance’s memory can’t be dropped either, because the other `Rc` instance still refers to it. The memory allocated to the list will remain uncollected forever. To visualize this reference cycle, we’ve created the diagram in Figure 15-4. -Reference cycle of lists +A rectangle labeled 'a' that points to a rectangle containing the integer 5. A rectangle labeled 'b' that points to a rectangle containing the integer 10. The rectangle containing 5 points to the rectangle containing 10, and the rectangle containing 10 points back to the rectangle containing 5, creating a cycle Figure 15-4: A reference cycle of lists `a` and `b` pointing to each other -If you uncomment the last `println!` and run the program, Rust will try to -print this cycle with `a` pointing to `b` pointing to `a`, and so forth, until -it overflows the stack. +If you uncomment the last `println!` and run the program, Rust will try to print +this cycle with `a` pointing to `b` pointing to `a` and so forth until it +overflows the stack. Compared to a real-world program, the consequences of creating a reference cycle in this example aren’t very dire: right after we create the reference cycle, @@ -1829,17 +1829,21 @@ Let’s look at an example using graphs made up of parent nodes and child nodes to see when non-ownership relationships are an appropriate way to prevent reference cycles. -### Preventing Reference Cycles: Turning an Rc into a Weak + + + + +### Preventing Reference Cycles Using Weak -So far, we’ve demonstrated that calling `Rc::clone` increases the -`strong_count` of an `Rc` instance, and an `Rc` instance is only cleaned -up if its `strong_count` is 0. You can also create a *weak reference* to the -value within an `Rc` instance by calling `Rc::downgrade` and passing a -reference to the `Rc`. *Strong references* are how you can share ownership of -an `Rc` instance. *Weak references* don’t express an ownership relationship, -and their count doesn’t affect when an `Rc` instance is cleaned up. They -won’t cause a reference cycle because any cycle involving some weak references -will be broken once the strong reference count of values involved is 0. +So far, we’ve demonstrated that calling `Rc::clone` increases the `strong_count` +of an `Rc` instance, and an `Rc` instance is only cleaned up if its +`strong_count` is 0. You can also create a weak reference to the value within +an `Rc` instance by calling `Rc::downgrade` and passing a reference to the +`Rc`. *Strong references* are how you can share ownership of an `Rc` +instance. *Weak references* don’t express an ownership relationship, and their +count doesn’t affect when an `Rc` instance is cleaned up. They won’t cause a +reference cycle because any cycle involving some weak references will be broken +once the strong reference count of values involved is 0. When you call `Rc::downgrade`, you get a smart pointer of type `Weak`. Instead of increasing the `strong_count` in the `Rc` instance by 1, calling @@ -1849,7 +1853,7 @@ Instead of increasing the `strong_count` in the `Rc` instance by 1, calling `Rc` instance to be cleaned up. Because the value that `Weak` references might have been dropped, to do -anything with the value that a `Weak` is pointing to, you must make sure the +anything with the value that a `Weak` is pointing to you must make sure the value still exists. Do this by calling the `upgrade` method on a `Weak` instance, which will return an `Option>`. You’ll get a result of `Some` if the `Rc` value has not been dropped yet and a result of `None` if the @@ -1861,7 +1865,7 @@ As an example, rather than using a list whose items know only about the next item, we’ll create a tree whose items know about their children items *and* their parent items. -#### Creating a Tree Data Structure: a Node with Child Nodes +#### Creating a Tree Data Structure: A Node with Child Nodes To start, we’ll build a tree with nodes that know about their child nodes. We’ll create a struct named `Node` that holds its own `i32` value as well as @@ -1887,8 +1891,8 @@ modify which nodes are children of another node, so we have a `RefCell` in `children` around the `Vec>`. Next, we’ll use our struct definition and create one `Node` instance named -`leaf` with the value 3 and no children, and another instance named `branch` -with the value 5 and `leaf` as one of its children, as shown in Listing 15-27: +`leaf` with the value `3` and no children, and another instance named `branch` +with the value `5` and `leaf` as one of its children, as shown in Listing 15-27. src/main.rs @@ -1949,7 +1953,7 @@ struct Node { A node will be able to refer to its parent node but doesn’t own its parent. In Listing 15-28, we update `main` to use this new definition so the `leaf` -node will have a way to refer to its parent, `branch`: +node will have a way to refer to its parent, `branch`. src/main.rs @@ -1975,7 +1979,7 @@ fn main() { } ``` -Listing 15-28: A `leaf` node with a weak reference to its parent node `branch` +Listing 15-28: A `leaf` node with a weak reference to its parent node, `branch` Creating the `leaf` node looks similar to Listing 15-27 with the exception of the `parent` field: `leaf` starts out without a parent, so we create a new, @@ -1990,7 +1994,7 @@ leaf parent = None ``` When we create the `branch` node, it will also have a new `Weak` -reference in the `parent` field, because `branch` doesn’t have a parent node. +reference in the `parent` field because `branch` doesn’t have a parent node. We still have `leaf` as one of the children of `branch`. Once we have the `Node` instance in `branch`, we can modify `leaf` to give it a `Weak` reference to its parent. We use the `borrow_mut` method on the @@ -2019,7 +2023,7 @@ Let’s look at how the `strong_count` and `weak_count` values of the `Rc` instances change by creating a new inner scope and moving the creation of `branch` into that scope. By doing so, we can see what happens when `branch` is created and then dropped when it goes out of scope. The modifications are shown -in Listing 15-29: +in Listing 15-29. src/main.rs @@ -2072,10 +2076,10 @@ Listing 15-29: Creating `branch` in an inner scope and examining strong and weak After `leaf` is created, its `Rc` has a strong count of 1 and a weak count of 0. In the inner scope, we create `branch` and associate it with -`leaf`, at which point, when we print the counts, the `Rc` in `branch` +`leaf`, at which point when we print the counts, the `Rc` in `branch` will have a strong count of 1 and a weak count of 1 (for `leaf.parent` pointing to `branch` with a `Weak`). When we print the counts in `leaf`, we’ll see -it will have a strong count of 2, because `branch` now has a clone of the +it will have a strong count of 2 because `branch` now has a clone of the `Rc` of `leaf` stored in `branch.children`, but will still have a weak count of 0. @@ -2086,7 +2090,7 @@ don’t get any memory leaks! If we try to access the parent of `leaf` after the end of the scope, we’ll get `None` again. At the end of the program, the `Rc` in `leaf` has a strong -count of 1 and a weak count of 0, because the variable `leaf` is now the only +count of 1 and a weak count of 0 because the variable `leaf` is now the only reference to the `Rc` again. All of the logic that manages the counts and value dropping is built into diff --git a/nostarch/docx/chapter15.docx b/nostarch/docx/chapter15.docx index 444900c045..6869bd9898 100644 Binary files a/nostarch/docx/chapter15.docx and b/nostarch/docx/chapter15.docx differ diff --git a/src/ch15-00-smart-pointers.md b/src/ch15-00-smart-pointers.md index 08ddcf8dc6..5f86585fd5 100644 --- a/src/ch15-00-smart-pointers.md +++ b/src/ch15-00-smart-pointers.md @@ -21,13 +21,6 @@ Rust, with its concept of ownership and borrowing, has an additional difference between references and smart pointers: while references only borrow data, in many cases smart pointers _own_ the data they point to. -Though we didn’t call them as such at the time, we’ve already encountered a few -smart pointers in this book, including `String` and `Vec` in Chapter 8. Both -of these types count as smart pointers because they own some memory and allow -you to manipulate it. They also have metadata and extra capabilities or -guarantees. `String`, for example, stores its capacity as metadata and has the -extra ability to ensure its data will always be valid UTF-8. - Smart pointers are usually implemented using structs. Unlike an ordinary struct, smart pointers implement the `Deref` and `Drop` traits. The `Deref` trait allows an instance of the smart pointer struct to behave like a reference @@ -48,6 +41,6 @@ cover the most common smart pointers in the standard library: In addition, we’ll cover the _interior mutability_ pattern where an immutable type exposes an API for mutating an interior value. We’ll also discuss -_reference cycles_: how they can leak memory and how to prevent them. +reference cycles: how they can leak memory and how to prevent them. Let’s dive in! diff --git a/src/ch15-01-box.md b/src/ch15-01-box.md index d9b7fb79c3..d48b7b7863 100644 --- a/src/ch15-01-box.md +++ b/src/ch15-01-box.md @@ -1,9 +1,9 @@ ## Using `Box` to Point to Data on the Heap -The most straightforward smart pointer is a _box_, whose type is written -`Box`. Boxes allow you to store data on the heap rather than the stack. What -remains on the stack is the pointer to the heap data. Refer to Chapter 4 to -review the difference between the stack and the heap. +The most straightforward smart pointer is a box, whose type is written +`Box`. _Boxes_ allow you to store data on the heap rather than the stack. +What remains on the stack is the pointer to the heap data. Refer to Chapter 4 +to review the difference between the stack and the heap. Boxes don’t have performance overhead, other than storing their data on the heap instead of on the stack. But they don’t have many extra capabilities @@ -54,7 +54,7 @@ Putting a single value on the heap isn’t very useful, so you won’t use boxes themselves in this way very often. Having values like a single `i32` on the stack, where they’re stored by default, is more appropriate in the majority of situations. Let’s look at a case where boxes allow us to define types that we -wouldn’t be allowed to if we didn’t have boxes. +wouldn’t be allowed to define if we didn’t have boxes. ### Enabling Recursive Types with Boxes @@ -136,7 +136,7 @@ is one more `Cons` value that holds `3` and a `List` value, which is finally If we try to compile the code in Listing 15-3, we get the error shown in Listing 15-4. -+ ```console {{#include ../listings/ch15-smart-pointers/listing-15-03/output.txt}} @@ -175,7 +175,7 @@ type needs, the compiler looks at the variants, starting with the `Cons` variant. The `Cons` variant holds a value of type `i32` and a value of type `List`, and this process continues infinitely, as shown in Figure 15-1. -An infinite Cons list +An infinite Cons list: a rectangle labeled 'Cons' split into two smaller rectangles. The first smaller rectangle holds the label 'i32', and the second smaller rectangle holds the label 'Cons' and a smaller version of the outer 'Cons' rectangle. The 'Cons' rectangles continue to hold smaller and smaller versions of themselves until the smallest comfortably-sized rectangle holds an infinity symbol, indicating that this repetition goes on forever Figure 15-1: An infinite `List` consisting of infinite `Cons` variants @@ -222,13 +222,13 @@ of the `List` in Listing 15-3 to the code in Listing 15-5, which will compile. The `Cons` variant needs the size of an `i32` plus the space to store the box’s pointer data. The `Nil` variant stores no values, so it needs less space -than the `Cons` variant. We now know that any `List` value will take up the -size of an `i32` plus the size of a box’s pointer data. By using a box, we’ve -broken the infinite, recursive chain, so the compiler can figure out the size -it needs to store a `List` value. Figure 15-2 shows what the `Cons` variant -looks like now. +on the stack than the `Cons` variant. We now know that any `List` value will +take up the size of an `i32` plus the size of a box’s pointer data. By using a +box, we’ve broken the infinite, recursive chain, so the compiler can figure out +the size it needs to store a `List` value. Figure 15-2 shows what the `Cons` +variant looks like now. -A finite Cons list +A rectangle labeled 'Cons' split into two smaller rectangles. The first smaller rectangle holds the label 'i32', and the second smaller rectangle holds the label 'Box' with one inner rectangle that contains the label 'usize', representing the finite size of the box's pointer Figure 15-2: A `List` that is not infinitely sized because `Cons` holds a `Box` diff --git a/src/ch15-02-deref.md b/src/ch15-02-deref.md index 17f89e1f76..a4127adf67 100644 --- a/src/ch15-02-deref.md +++ b/src/ch15-02-deref.md @@ -18,11 +18,6 @@ smart pointers to work in ways similar to references. Then we’ll look at Rust’s _deref coercion_ feature and how it lets us work with either references or smart pointers. -> Note: There’s one big difference between the `MyBox` type we’re about to -> build and the real `Box`: our version will not store its data on the heap. -> We are focusing this example on `Deref`, so where the data is actually stored -> is less important than the pointer-like behavior. - @@ -66,7 +61,7 @@ to the value it’s pointing to. We can rewrite the code in Listing 15-6 to use a `Box` instead of a reference; the dereference operator used on the `Box` in Listing 15-7 functions in the same way as the dereference operator used on the reference in -Listing 15-6: +Listing 15-6. @@ -81,15 +76,20 @@ The main difference between Listing 15-7 and Listing 15-6 is that here we set reference pointing to the value of `x`. In the last assertion, we can use the dereference operator to follow the box’s pointer in the same way that we did when `y` was a reference. Next, we’ll explore what is special about `Box` -that enables us to use the dereference operator by defining our own type. +that enables us to use the dereference operator by defining our own box type. ### Defining Our Own Smart Pointer -Let’s build a smart pointer similar to the `Box` type provided by the -standard library to experience how smart pointers behave differently from +Let’s build a wrapper type similar to the `Box` type provided by the +standard library to experience how smart pointer types behave differently from references by default. Then we’ll look at how to add the ability to use the dereference operator. +> Note: There’s one big difference between the `MyBox` type we’re about to +> build and the real `Box`: our version will not store its data on the heap. +> We are focusing this example on `Deref`, so where the data is actually stored +> is less important than the pointer-like behavior. + The `Box` type is ultimately defined as a tuple struct with one element, so Listing 15-8 defines a `MyBox` type in the same way. We’ll also define a `new` function to match the `new` function defined on `Box`. @@ -137,7 +137,7 @@ implement the `Deref` trait. ### Implementing the `Deref` Trait As discussed in [“Implementing a Trait on a Type”][impl-trait] in -Chapter 10, to implement a trait, we need to provide implementations for the +Chapter 10, to implement a trait we need to provide implementations for the trait’s required methods. The `Deref` trait, provided by the standard library, requires us to implement one method named `deref` that borrows `self` and returns a reference to the inner data. Listing 15-10 contains an implementation diff --git a/src/ch15-04-rc.md b/src/ch15-04-rc.md index 23ffa46a89..bd743c000b 100644 --- a/src/ch15-04-rc.md +++ b/src/ch15-04-rc.md @@ -35,7 +35,7 @@ Let’s return to our cons list example in Listing 15-5. Recall that we defined it using `Box`. This time, we’ll create two lists that both share ownership of a third list. Conceptually, this looks similar to Figure 15-3. -Two lists that share ownership of a third list +A linked list with the label 'a' pointing to three elements: the first element contains the integer 5 and points to the second element. The second element contains the integer 10 and points to the third element. The third element contains the value 'Nil' that signifies the end of the list; it does not point anywhere. A linked list with the label 'b' points to an element that contains the integer 3 and points to the first element of list 'a'. A linked list with the label 'c' points to an element that contains the integer 4 and also points to the first element of list 'a', so that the tail of lists 'b' and 'c' are both list 'a' Figure 15-3: Two lists, `b` and `c`, sharing ownership of a third list, `a` @@ -46,9 +46,9 @@ lists will then continue on to the first `a` list containing `5` and `10`. In other words, both lists will share the first list containing `5` and `10`. Trying to implement this scenario using our definition of `List` with `Box` -won’t work, as shown in Listing 15-17: +won’t work, as shown in Listing 15-17. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-17/src/main.rs}} @@ -92,8 +92,8 @@ it. We need to add a `use` statement to bring `Rc` into scope because it’s not -in the prelude. In `main`, we create the list holding 5 and 10 and store it in -a new `Rc` in `a`. Then when we create `b` and `c`, we call the +in the prelude. In `main`, we create the list holding `5` and `10` and store it +in a new `Rc` in `a`. Then, when we create `b` and `c`, we call the `Rc::clone` function and pass a reference to the `Rc` in `a` as an argument. diff --git a/src/ch15-05-interior-mutability.md b/src/ch15-05-interior-mutability.md index e1f5b0225e..2e02265857 100644 --- a/src/ch15-05-interior-mutability.md +++ b/src/ch15-05-interior-mutability.md @@ -47,8 +47,8 @@ beyond the scope of this book but is an interesting topic to research. Because some analysis is impossible, if the Rust compiler can’t be sure the code complies with the ownership rules, it might reject a correct program; in this way, it’s conservative. If Rust accepted an incorrect program, users -wouldn’t be able to trust in the guarantees Rust makes. However, if Rust -rejects a correct program, the programmer will be inconvenienced, but nothing +wouldn’t be able to trust the guarantees Rust makes. However, if Rust rejects a +correct program, the programmer will be inconvenienced, but nothing catastrophic can occur. The `RefCell` type is useful when you’re sure your code follows the borrowing rules but the compiler is unable to understand and guarantee that. @@ -146,7 +146,7 @@ is that we want to test the behavior of the `set_value` method on the `set_value` doesn’t return anything for us to make assertions on. We want to be able to say that if we create a `LimitTracker` with something that implements the `Messenger` trait and a particular value for `max`, when we pass different -numbers for `value`, the messenger is told to send the appropriate messages. +numbers for `value` the messenger is told to send the appropriate messages. We need a mock object that, instead of sending an email or text message when we call `send`, will only keep track of the messages it’s told to send. We can @@ -173,7 +173,7 @@ take the message passed in as a parameter and store it in the `MockMessenger` list of `sent_messages`. In the test, we’re testing what happens when the `LimitTracker` is told to set -`value` to something that is more than 75 percent of the `max` value. First, we +`value` to something that is more than 75 percent of the `max` value. First we create a new `MockMessenger`, which will start with an empty list of messages. Then we create a new `LimitTracker` and give it a reference to the new `MockMessenger` and a `max` value of `100`. We call the `set_value` method on @@ -187,12 +187,12 @@ However, there’s one problem with this test, as shown here: {{#include ../listings/ch15-smart-pointers/listing-15-21/output.txt}} ``` -We can’t modify the `MockMessenger` to keep track of the messages, because the +We can’t modify the `MockMessenger` to keep track of the messages because the `send` method takes an immutable reference to `self`. We also can’t take the suggestion from the error text to use `&mut self` in both the `impl` method and -the `trait` definition. We do not want to change the `Messenger` trait solely -for the sake of testing. Instead, we need to find a way to make our test code -work correctly with our existing design. +the trait definition. We do not want to change the `Messenger` trait solely for +the sake of testing. Instead, we need to find a way to make our test code work +correctly with our existing design. This is a situation in which interior mutability can help! We’ll store the `sent_messages` within a `RefCell`, and then the `send` method will be diff --git a/src/ch15-06-reference-cycles.md b/src/ch15-06-reference-cycles.md index ba4ec0b570..5402e16bc5 100644 --- a/src/ch15-06-reference-cycles.md +++ b/src/ch15-06-reference-cycles.md @@ -66,14 +66,14 @@ The reference count of the `Rc` instances in both `a` and `b` is 2 after we change the list in `a` to point to `b`. At the end of `main`, Rust drops the variable `b`, which decreases the reference count of the `b` `Rc` instance from 2 to 1. The memory that `Rc` has on the heap won’t be dropped at -this point, because its reference count is 1, not 0. Then Rust drops `a`, which +this point because its reference count is 1, not 0. Then Rust drops `a`, which decreases the reference count of the `a` `Rc` instance from 2 to 1 as well. This instance’s memory can’t be dropped either, because the other `Rc` instance still refers to it. The memory allocated to the list will remain uncollected forever. To visualize this reference cycle, we’ve created the diagram in Figure 15-4. -Reference cycle of lists +A rectangle labeled 'a' that points to a rectangle containing the integer 5. A rectangle labeled 'b' that points to a rectangle containing the integer 10. The rectangle containing 5 points to the rectangle containing 10, and the rectangle containing 10 points back to the rectangle containing 5, creating a cycle Figure 15-4: A reference cycle of lists `a` and `b` pointing to each other @@ -115,10 +115,10 @@ reference cycles. So far, we’ve demonstrated that calling `Rc::clone` increases the `strong_count` of an `Rc` instance, and an `Rc` instance is only cleaned up if its -`strong_count` is 0. You can also create a _weak reference_ to the value within +`strong_count` is 0. You can also create a weak reference to the value within an `Rc` instance by calling `Rc::downgrade` and passing a reference to the -`Rc`. Strong references are how you can share ownership of an `Rc` -instance. Weak references don’t express an ownership relationship, and their +`Rc`. _Strong references_ are how you can share ownership of an `Rc` +instance. _Weak references_ don’t express an ownership relationship, and their count doesn’t affect when an `Rc` instance is cleaned up. They won’t cause a reference cycle because any cycle involving some weak references will be broken once the strong reference count of values involved is 0. @@ -184,7 +184,7 @@ parent. We’ll do that next. To make the child node aware of its parent, we need to add a `parent` field to our `Node` struct definition. The trouble is in deciding what the type of -`parent` should be. We know it can’t contain an `Rc` because that would +`parent` should be. We know it can’t contain an `Rc`, because that would create a reference cycle with `leaf.parent` pointing to `branch` and `branch.children` pointing to `leaf`, which would cause their `strong_count` values to never be 0. @@ -194,7 +194,7 @@ children: if a parent node is dropped, its child nodes should be dropped as well. However, a child should not own its parent: if we drop a child node, the parent should still exist. This is a case for weak references! -So, instead of `Rc`, we’ll make the type of `parent` use `Weak`, +So instead of `Rc`, we’ll make the type of `parent` use `Weak`, specifically a `RefCell>`. Now our `Node` struct definition looks like this: @@ -208,7 +208,7 @@ A node will be able to refer to its parent node but doesn’t own its parent. In Listing 15-28, we update `main` to use this new definition so the `leaf` node will have a way to refer to its parent, `branch`. -+ ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-28/src/main.rs:there}} @@ -229,7 +229,7 @@ leaf parent = None ``` When we create the `branch` node, it will also have a new `Weak` -reference in the `parent` field, because `branch` doesn’t have a parent node. +reference in the `parent` field because `branch` doesn’t have a parent node. We still have `leaf` as one of the children of `branch`. Once we have the `Node` instance in `branch`, we can modify `leaf` to give it a `Weak` reference to its parent. We use the `borrow_mut` method on the @@ -284,7 +284,7 @@ don’t get any memory leaks! If we try to access the parent of `leaf` after the end of the scope, we’ll get `None` again. At the end of the program, the `Rc` in `leaf` has a strong -count of 1 and a weak count of 0, because the variable `leaf` is now the only +count of 1 and a weak count of 0 because the variable `leaf` is now the only reference to the `Rc` again. All of the logic that manages the counts and value dropping is built into