summaryrefslogtreecommitdiffstats
path: root/src/doc/book/nostarch/chapter15.md
diff options
context:
space:
mode:
Diffstat (limited to 'src/doc/book/nostarch/chapter15.md')
-rw-r--r--src/doc/book/nostarch/chapter15.md675
1 files changed, 372 insertions, 303 deletions
diff --git a/src/doc/book/nostarch/chapter15.md b/src/doc/book/nostarch/chapter15.md
index 166277fab..c0e847da1 100644
--- a/src/doc/book/nostarch/chapter15.md
+++ b/src/doc/book/nostarch/chapter15.md
@@ -13,34 +13,34 @@ 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
smart pointers isn’t unique to Rust: smart pointers originated in C++ and exist
in other languages as well. Rust has a variety of smart pointers defined in the
standard library that provide functionality beyond that provided by references.
-To explore the general concept, we'll look at a couple of different examples of
+To explore the general concept, we’ll look at a couple of different examples of
smart pointers, including a *reference counting* smart pointer type. This
pointer enables you to allow data to have multiple owners by keeping track of
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.
+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
+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<T>` 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.
+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
so you can write your code to work with either references or smart pointers.
-The `Drop` trait allows you to customize the code that's run when an instance
+The `Drop` trait allows you to customize the code that’s run when an instance
of the smart pointer goes out of scope. In this chapter, we’ll discuss both
traits and demonstrate why they’re important to smart pointers.
@@ -49,10 +49,10 @@ 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<T>` for allocating values on the heap
+* `Box<T>`, for allocating values on the heap
* `Rc<T>`, a reference counting type that enables multiple ownership
* `Ref<T>` and `RefMut<T>`, accessed through `RefCell<T>`, a type that enforces
- the borrowing rules at runtime instead of compile time
+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
@@ -60,7 +60,7 @@ type exposes an API for mutating an interior value. We’ll also discuss
Let’s dive in!
-## Using `Box<T>` to Point to Data on the Heap
+## Using Box<T> to Point to Data on the Heap
The most straightforward smart pointer is a *box*, whose type is written
`Box<T>`. Boxes allow you to store data on the heap rather than the stack. What
@@ -72,35 +72,35 @@ heap instead of on the stack. But they don’t have many extra capabilities
either. You’ll use them most often in these situations:
* When you have a type whose size can’t be known at compile time and you want
- to use a value of that type in a context that requires an exact size
+to use a value of that type in a context that requires an exact size
* When you have a large amount of data and you want to transfer ownership but
- ensure the data won’t be copied when you do so
+ensure the data won’t be copied when you do so
* When you want to own a value and you care only that it’s a type that
- implements a particular trait rather than being of a specific type
+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
+We’ll demonstrate the first situation in “Enabling Recursive Types with Boxes”
+on page XX. In the second case, transferring ownership of a large amount of
data can take a long time because the data is copied around on the stack. To
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 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!
+third case is known as a *trait object*, and “Using Trait Objects That Allow
+for Values of Different Types” on page XX is devoted to that topic. So what you
+learn here you’ll apply again in that section!
-### Using a `Box<T>` to Store Data on the Heap
+### Using Box<T> to Store Data on the Heap
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>`.
-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.
Filename: src/main.rs
```
fn main() {
let b = Box::new(5);
- println!("b = {}", b);
+ println!("b = {b}");
}
```
@@ -118,34 +118,34 @@ 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
+A value of a *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.
-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
-we’ll define is straightforward except for the recursion; therefore, the
+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 we’ll define is straightforward except for the recursion; therefore, the
concepts in the example we’ll work with will be useful any time you get into
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:
+For example, here’s a pseudocode representation of a cons list containing the
+list `1, 2, 3` with each pair in parentheses:
```
(1, (2, (3, Nil)))
@@ -181,16 +181,18 @@ Listing 15-2: The first attempt at defining an enum to represent a cons list
data structure of `i32` values
> Note: We’re implementing a cons list that holds only `i32` values for the
-> purposes of this example. We could have implemented it using generics, as we
-> discussed in Chapter 10, to define a cons list type that could store values of
-> any type.
+purposes of this example. We could have implemented it using generics, as we
+discussed in Chapter 10, to define a cons list type that could store values of
+any type.
Using the `List` type to store the list `1, 2, 3` would look like the code in
-Listing 15-3:
+Listing 15-3.
Filename: src/main.rs
```
+--snip--
+
use crate::List::{Cons, Nil};
fn main() {
@@ -206,7 +208,7 @@ 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.
```
error[E0072]: recursive type `List` has infinite size
@@ -217,7 +219,11 @@ error[E0072]: recursive type `List` has infinite size
2 | Cons(i32, List),
| ---- recursive without indirection
|
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List`
+representable
+ |
+2 | Cons(i32, Box<List>),
+ | ++++ +
```
Listing 15-4: The error we get when attempting to define a recursive enum
@@ -225,7 +231,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
@@ -258,23 +264,22 @@ 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.
-<img alt="An infinite Cons list" src="img/trpl15-01.svg" class="center" style="width: 50%;" />
-
Figure 15-1: An infinite `List` consisting of infinite `Cons` variants
-#### Using `Box<T>` to Get a Recursive Type with a Known Size
+#### Using Box<T> to Get a Recursive Type with a Known Size
Because Rust can’t figure out how much space to allocate for recursively
defined types, the compiler gives an error with this helpful suggestion:
```
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List` representable
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List`
+representable
|
2 | Cons(i32, Box<List>),
- | ^^^^ ^
+ | ++++ +
```
-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.
@@ -288,7 +293,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.
Filename: src/main.rs
@@ -301,31 +306,38 @@ enum List {
use crate::List::{Cons, Nil};
fn main() {
- let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
+ let list = Cons(
+ 1,
+ Box::new(Cons(
+ 2,
+ Box::new(Cons(
+ 3,
+ Box::new(Nil)
+ ))
+ ))
+ );
}
```
Listing 15-5: Definition of `List` that uses `Box<T>` in order to have a known
size
-The `Cons` variant needs the size of an `i32` plus the space to store the
-box’s pointer data. The `Nil` variant stores no values, so it needs less space
-than 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.
+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.
-<img alt="A finite Cons list" src="img/trpl15-02.svg" class="center" />
-
-Figure 15-2: A `List` that is not infinitely sized because `Cons` holds a `Box`
+Figure 15-2: A `List` that is not infinitely sized, because `Cons` holds a `Box`
Boxes provide only the indirection and heap allocation; they don’t have any
other special capabilities, like those we’ll see with the other smart pointer
types. They also don’t have the performance overhead that these special
capabilities incur, so they can be useful in cases like the cons list where the
indirection is the only feature we need. We’ll look at more use cases for boxes
-in Chapter 17, too.
+in Chapter 17.
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>`
@@ -335,7 +347,7 @@ 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
@@ -347,31 +359,31 @@ Let’s first look at how the dereference operator works with regular references
Then we’ll try to define a custom type that behaves like `Box<T>`, and see why
the dereference operator doesn’t work like a reference on our newly defined
type. We’ll explore how implementing the `Deref` trait makes it possible for
-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.
+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<T>` type we’re about to
-> build and the real `Box<T>`: 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.
+> Note: There’s one big difference between the `MyBox<T>` type we’re about to
+build and the real `Box<T>`: 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
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.
Filename: src/main.rs
```
fn main() {
- [1] let x = 5;
- [2] let y = &x;
+ 1 let x = 5;
+ 2 let y = &x;
- [3] assert_eq!(5, x);
- [4] assert_eq!(5, *y);
+ 3 assert_eq!(5, x);
+ 4 assert_eq!(5, *y);
}
```
@@ -393,31 +405,33 @@ error[E0277]: can't compare `{integer}` with `&{integer}`
--> src/main.rs:6:5
|
6 | assert_eq!(5, y);
- | ^^^^^^^^^^^^^^^^ no implementation for `{integer} == &{integer}`
+ | ^^^^^^^^^^^^^^^^ no implementation for `{integer} ==
+&{integer}`
|
- = help: the trait `PartialEq<&{integer}>` is not implemented for `{integer}`
+ = help: the trait `PartialEq<&{integer}>` is not implemented
+for `{integer}`
```
Comparing a number and a reference to a number isn’t allowed because they’re
different types. We must use the dereference operator to follow the reference
to the value it’s pointing to.
-### Using `Box<T>` Like a Reference
+### Using Box<T> Like a Reference
We can rewrite the code in Listing 15-6 to use a `Box<T>` instead of a
reference; the dereference operator used on the `Box<T>` in Listing 15-7
functions in the same way as the dereference operator used on the reference in
-Listing 15-6:
+Listing 15-6.
Filename: src/main.rs
```
fn main() {
let x = 5;
- [1] let y = Box::new(x);
+ 1 let y = Box::new(x);
assert_eq!(5, x);
- [2] assert_eq!(5, *y);
+ 2 assert_eq!(5, *y);
}
```
@@ -445,18 +459,18 @@ Listing 15-8 defines a `MyBox<T>` type in the same way. We’ll also define a
Filename: src/main.rs
```
-[1] struct MyBox<T>(T);
+ 1 struct MyBox<T>(T);
impl<T> MyBox<T> {
- [2] fn new(x: T) -> MyBox<T> {
- [3] MyBox(x)
+ 2 fn new(x: T) -> MyBox<T> {
+ 3 MyBox(x)
}
}
```
Listing 15-8: Defining a `MyBox<T>` type
-We define a struct named `MyBox` and declare a generic parameter `T` [1],
+We define a struct named `MyBox` and declare a generic parameter `T` [1]
because we want our type to hold values of any type. The `MyBox` type is a
tuple struct with one element of type `T`. The `MyBox::new` function takes one
parameter of type `T` [2] and returns a `MyBox` instance that holds the value
@@ -482,7 +496,7 @@ fn main() {
Listing 15-9: Attempting to use `MyBox<T>` in the same way we used references
and `Box<T>`
-Here’s the resulting compilation error:
+Here’s the resultant compilation error:
```
error[E0614]: type `MyBox<{integer}>` cannot be dereferenced
@@ -496,14 +510,14 @@ Our `MyBox<T>` type can’t be dereferenced because we haven’t implemented tha
ability on our type. To enable dereferencing with the `*` operator, we
implement the `Deref` trait.
-### Treating a Type Like a Reference by Implementing the `Deref` Trait
+### Implementing the Deref Trait
-As discussed in the “Implementing a Trait on a Type” section of Chapter 10, to
-implement a trait, we need to provide implementations for the trait’s required
-methods. The `Deref` trait, provided by the standard library, requires us to
-implement one method named `deref` that borrows `self` and returns a reference
-to the inner data. Listing 15-10 contains an implementation of `Deref` to add
-to the definition of `MyBox`:
+As discussed in “Implementing a Trait on a Type” on page XX, 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``<T>`.
Filename: src/main.rs
@@ -511,10 +525,10 @@ Filename: src/main.rs
use std::ops::Deref;
impl<T> Deref for MyBox<T> {
- [1] type Target = T;
+ 1 type Target = T;
fn deref(&self) -> &Self::Target {
- [2] &self.0
+ 2 &self.0
}
}
```
@@ -528,10 +542,10 @@ them in more detail in Chapter 19.
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 [2]; 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<T>` value now
-compiles, and the assertions pass!
+“Using Tuple Structs Without Named Fields to Create Different Types” on page XX
+that `.0` accesses the first value in a tuple struct. The `main` function in
+Listing 15-9 that calls `*` on the `MyBox<T>` 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
@@ -553,7 +567,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<T>` in
this case or in most cases where we use the dereference operator.
@@ -584,7 +598,7 @@ can work for either references or smart pointers.
To see deref coercion in action, let’s use the `MyBox<T>` 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.
Filename: src/main.rs
@@ -597,8 +611,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<String>`, 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<String>`, as shown in Listing 15-12.
Filename: src/main.rs
@@ -661,10 +675,10 @@ cases:
* From `&mut T` to `&mut U` when `T: DerefMut<Target=U>`
* From `&mut T` to `&U` when `T: Deref<Target=U>`
-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
@@ -678,12 +692,12 @@ the borrowing rules don’t guarantee that. Therefore, Rust can’t make the
assumption that converting an immutable reference to a mutable reference is
possible.
-## Running Code on Cleanup with the `Drop` Trait
+## Running Code on Cleanup with the Drop Trait
The second trait important to the smart pointer pattern is `Drop`, which lets
you customize what happens when a value is about to go out of scope. You can
-provide an implementation for the `Drop` trait on any type, and that code
-can be used to release resources like files or network connections.
+provide an implementation for the `Drop` trait on any type, and that code can
+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
@@ -692,7 +706,7 @@ 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
@@ -706,7 +720,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.
Filename: src/main.rs
@@ -715,21 +729,24 @@ struct CustomSmartPointer {
data: String,
}
-[1] impl Drop for CustomSmartPointer {
+1 impl Drop for CustomSmartPointer {
fn drop(&mut self) {
- [2] println!("Dropping CustomSmartPointer with data `{}`!", self.data);
+ 2 println!(
+ "Dropping CustomSmartPointer with data `{}`!",
+ self.data
+ );
}
}
fn main() {
- [3] let c = CustomSmartPointer {
+ 3 let c = CustomSmartPointer {
data: String::from("my stuff"),
};
- [4] let d = CustomSmartPointer {
+ 4 let d = CustomSmartPointer {
data: String::from("other stuff"),
};
- [5] println!("CustomSmartPointers created.");
-[6] }
+ 5 println!("CustomSmartPointers created.");
+6 }
```
Listing 15-14: A `CustomSmartPointer` struct that implements the `Drop` trait
@@ -738,12 +755,12 @@ where we would put our cleanup code
The `Drop` trait is included in the prelude, so we don’t need to bring it into
scope. We implement the `Drop` trait on `CustomSmartPointer` [1] and provide an
implementation for the `drop` method that calls `println!` [2]. 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` [3][4] and then
-print `CustomSmartPointers created` [5]. At the end of `main` [6], our
+In `main`, we create two instances of `CustomSmartPointer` at [3] and [4] and
+then print `CustomSmartPointers created` [5]. At the end of `main` [6], our
instances of `CustomSmartPointer` will go out of scope, and Rust will call the
code we put in the `drop` method [2], printing our final message. Note that we
didn’t need to call the `drop` method explicitly.
@@ -758,26 +775,24 @@ Dropping CustomSmartPointer with data `my stuff`!
Rust automatically called `drop` for us when our instances went out of scope,
calling the code we specified. Variables are dropped in the reverse order of
-their creation, so `d` was dropped before `c`. This example's purpose is to
+their creation, so `d` was dropped before `c`. This example’s purpose is to
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
`Drop` trait is that it’s taken care of automatically. Occasionally, however,
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.
Filename: src/main.rs
@@ -788,7 +803,9 @@ fn main() {
};
println!("CustomSmartPointer created.");
c.drop();
- println!("CustomSmartPointer dropped before the end of main.");
+ println!(
+ "CustomSmartPointer dropped before the end of main."
+ );
}
```
@@ -805,6 +822,7 @@ error[E0040]: explicit use of destructor method
| --^^^^--
| | |
| | explicit destructor calls not allowed
+ | help: consider using `drop` function: `drop(c)`
```
This error message states that we’re not allowed to explicitly call `drop`. The
@@ -823,9 +841,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.
Filename: src/main.rs
@@ -836,7 +854,9 @@ fn main() {
};
println!("CustomSmartPointer created.");
drop(c);
- println!("CustomSmartPointer dropped before the end of main.");
+ println!(
+ "CustomSmartPointer dropped before the end of main."
+ );
}
```
@@ -851,7 +871,7 @@ Dropping CustomSmartPointer with data `some data`!
CustomSmartPointer dropped before the end of main.
```
-The text ```Dropping CustomSmartPointer with data `some data`!``` is printed
+The text `Dropping CustomSmartPointer with data `some data`!` is printed
between the `CustomSmartPointer created.` and `CustomSmartPointer dropped
before the end of main.` text, showing that the `drop` method code is called to
drop `c` at that point.
@@ -870,7 +890,7 @@ Now that we’ve examined `Box<T>` and some of the characteristics of smart
pointers, let’s look at a few other smart pointers defined in the standard
library.
-## `Rc<T>`, the Reference Counted Smart Pointer
+## Rc<T>, the Reference Counted Smart Pointer
In the majority of cases, ownership is clear: you know exactly which variable
owns a given value. However, there are cases when a single value might have
@@ -889,7 +909,7 @@ Imagine `Rc<T>` 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<T>` 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
@@ -901,23 +921,21 @@ Note that `Rc<T>` is only for use in single-threaded scenarios. When we discuss
concurrency in Chapter 16, we’ll cover how to do reference counting in
multithreaded programs.
-### Using `Rc<T>` to Share Data
+### Using Rc<T> to Share Data
Let’s return to our cons list example in Listing 15-5. Recall that we defined
it using `Box<T>`. This time, we’ll create two lists that both share ownership
-of a third list. Conceptually, this looks similar to Figure 15-3:
-
-<img alt="Two lists that share ownership of a third list" src="img/trpl15-03.svg" class="center" />
+of a third list. Conceptually, this looks similar to Figure 15-3.
Figure 15-3: Two lists, `b` and `c`, sharing ownership of a third list, `a`
-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<T>`
-won’t work, as shown in Listing 15-17:
+won’t work, as shown in Listing 15-17.
Filename: src/main.rs
@@ -931,13 +949,13 @@ use crate::List::{Cons, Nil};
fn main() {
let a = Cons(5, Box::new(Cons(10, Box::new(Nil))));
-[1] let b = Cons(3, Box::new(a));
-[2] let c = Cons(4, Box::new(a));
+ 1 let b = Cons(3, Box::new(a));
+ 2 let c = Cons(4, Box::new(a));
}
```
-Listing 15-17: Demonstrating we’re not allowed to have two lists using `Box<T>`
-that try to share ownership of a third list
+Listing 15-17: Demonstrating that we’re not allowed to have two lists using
+`Box<T>` that try to share ownership of a third list
When we compile this code, we get this error:
@@ -946,7 +964,8 @@ error[E0382]: use of moved value: `a`
--> src/main.rs:11:30
|
9 | let a = Cons(5, Box::new(Cons(10, Box::new(Nil))));
- | - move occurs because `a` has type `List`, which does not implement the `Copy` trait
+ | - move occurs because `a` has type `List`, which
+does not implement the `Copy` trait
10 | let b = Cons(3, Box::new(a));
| - value moved here
11 | let c = Cons(4, Box::new(a));
@@ -983,22 +1002,22 @@ enum List {
}
use crate::List::{Cons, Nil};
-[1] use std::rc::Rc;
+1 use std::rc::Rc;
fn main() {
-[2] let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
-[3] let b = Cons(3, Rc::clone(&a));
-[4] let c = Cons(4, Rc::clone(&a));
+ 2 let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
+ 3 let b = Cons(3, Rc::clone(&a));
+ 4 let c = Cons(4, Rc::clone(&a));
}
```
Listing 15-18: A definition of `List` that uses `Rc<T>`
We need to add a `use` statement to bring `Rc<T>` into scope [1] because it’s
-not in the prelude. In `main`, we create the list holding 5 and 10 and store it
-in a new `Rc<List>` in `a` [2]. Then when we create `b` [3] and `c` [4], we
-call the `Rc::clone` function and pass a reference to the `Rc<List>` in `a` as
-an argument.
+not in the prelude. In `main`, we create the list holding `5` and `10` and
+store it in a new `Rc<List>` in `a` [2]. Then, when we create `b` [3] and `c`
+[4], we call the `Rc::clone` function and pass a reference to the `Rc<List>` in
+`a` as an argument.
We could have called `a.clone()` rather than `Rc::clone(&a)`, but Rust’s
convention is to use `Rc::clone` in this case. The implementation of
@@ -1011,7 +1030,7 @@ increase the reference count. When looking for performance problems in the
code, we only need to consider the deep-copy clones and can disregard calls to
`Rc::clone`.
-### Cloning an `Rc<T>` Increases the Reference Count
+### Cloning an Rc<T> Increases the Reference Count
Let’s change our working example in Listing 15-18 so we can see the reference
counts changing as we create and drop references to the `Rc<List>` in `a`.
@@ -1022,16 +1041,30 @@ then we can see how the reference count changes when `c` goes out of scope.
Filename: src/main.rs
```
+--snip--
+
fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
- println!("count after creating a = {}", Rc::strong_count(&a));
+ println!(
+ "count after creating a = {}",
+ Rc::strong_count(&a)
+ );
let b = Cons(3, Rc::clone(&a));
- println!("count after creating b = {}", Rc::strong_count(&a));
+ println!(
+ "count after creating b = {}",
+ Rc::strong_count(&a)
+ );
{
let c = Cons(4, Rc::clone(&a));
- println!("count after creating c = {}", Rc::strong_count(&a));
+ println!(
+ "count after creating c = {}",
+ Rc::strong_count(&a)
+ );
}
- println!("count after c goes out of scope = {}", Rc::strong_count(&a));
+ println!(
+ "count after c goes out of scope = {}",
+ Rc::strong_count(&a)
+ );
}
```
@@ -1040,8 +1073,8 @@ Listing 15-19: Printing the reference count
At each point in the program where the reference count changes, we print the
reference count, which we get by calling the `Rc::strong_count` function. This
function is named `strong_count` rather than `count` because the `Rc<T>` type
-also has a `weak_count`; we’ll see what `weak_count` is used for in the
-“Preventing Reference Cycles: Turning an `Rc<T>` into a `Weak<T>`” section.
+also has a `weak_count`; we’ll see what `weak_count` is used for in “Preventing
+Reference Cycles Using Weak<T>” on page XX.
This code prints the following:
@@ -1074,7 +1107,7 @@ section, we’ll discuss the interior mutability pattern and the `RefCell<T>`
type that you can use in conjunction with an `Rc<T>` to work with this
immutability restriction.
-## `RefCell<T>` and the Interior Mutability Pattern
+## RefCell<T> and the Interior Mutability Pattern
*Interior mutability* is a design pattern in Rust that allows you to mutate
data even when there are immutable references to that data; normally, this
@@ -1092,14 +1125,14 @@ safe API, and the outer type is still immutable.
Let’s explore this concept by looking at the `RefCell<T>` type that follows the
interior mutability pattern.
-### Enforcing Borrowing Rules at Runtime with `RefCell<T>`
+### Enforcing Borrowing Rules at Runtime with RefCell<T>
Unlike `Rc<T>`, the `RefCell<T>` type represents single ownership over the data
-it holds. So, what makes `RefCell<T>` different from a type like `Box<T>`?
+it holds. So what makes `RefCell<T>` different from a type like `Box<T>`?
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<T>`, the borrowing rules’ invariants are enforced at
@@ -1137,13 +1170,13 @@ multithreaded program in Chapter 16.
Here is a recap of the reasons to choose `Box<T>`, `Rc<T>`, or `RefCell<T>`:
* `Rc<T>` enables multiple owners of the same data; `Box<T>` and `RefCell<T>`
- have single owners.
+have single owners.
* `Box<T>` allows immutable or mutable borrows checked at compile time; `Rc<T>`
- allows only immutable borrows checked at compile time; `RefCell<T>` allows
- immutable or mutable borrows checked at runtime.
+allows only immutable borrows checked at compile time; `RefCell<T>` allows
+immutable or mutable borrows checked at runtime.
* Because `RefCell<T>` allows mutable borrows checked at runtime, you can
- mutate the value inside the `RefCell<T>` even when the `RefCell<T>` is
- immutable.
+mutate the value inside the `RefCell<T>` even when the `RefCell<T>` is
+immutable.
Mutating the value inside an immutable value is the *interior mutability*
pattern. Let’s look at a situation in which interior mutability is useful and
@@ -1154,6 +1187,8 @@ examine how it’s possible.
A consequence of the borrowing rules is that when you have an immutable value,
you can’t borrow it mutably. For example, this code won’t compile:
+Filename: src/main.rs
+
```
fn main() {
let x = 5;
@@ -1164,7 +1199,8 @@ fn main() {
If you tried to compile this code, you’d get the following error:
```
-error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
+error[E0596]: cannot borrow `x` as mutable, as it is not declared
+as mutable
--> src/main.rs:3:13
|
2 | let x = 5;
@@ -1188,13 +1224,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
@@ -1210,15 +1246,15 @@ 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:
+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.
Filename: src/lib.rs
```
pub trait Messenger {
-[1] fn send(&self, msg: &str);
+ 1 fn send(&self, msg: &str);
}
pub struct LimitTracker<'a, T: Messenger> {
@@ -1231,7 +1267,10 @@ impl<'a, T> LimitTracker<'a, T>
where
T: Messenger,
{
- pub fn new(messenger: &'a T, max: usize) -> LimitTracker<'a, T> {
+ pub fn new(
+ messenger: &'a T,
+ max: usize
+ ) -> LimitTracker<'a, T> {
LimitTracker {
messenger,
value: 0,
@@ -1239,19 +1278,21 @@ where
}
}
-[2] pub fn set_value(&mut self, value: usize) {
+ 2 pub fn set_value(&mut self, value: usize) {
self.value = value;
- let percentage_of_max = self.value as f64 / self.max as f64;
+ let percentage_of_max =
+ self.value as f64 / self.max as f64;
if percentage_of_max >= 1.0 {
- self.messenger.send("Error: You are over your quota!");
+ self.messenger
+ .send("Error: You are over your quota!");
} else if percentage_of_max >= 0.9 {
self.messenger
- .send("Urgent warning: You've used up over 90% of your quota!");
+ .send("Urgent: You're at 90% of your quota!");
} else if percentage_of_max >= 0.75 {
self.messenger
- .send("Warning: You've used up over 75% of your quota!");
+ .send("Warning: You're at 75% of your quota!");
}
}
}
@@ -1269,7 +1310,7 @@ part is that we want to test the behavior of the `set_value` method on the
but `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
+different 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
@@ -1277,7 +1318,7 @@ 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.
Filename: src/lib.rs
@@ -1286,28 +1327,31 @@ Filename: src/lib.rs
mod tests {
use super::*;
- [1] struct MockMessenger {
- [2] sent_messages: Vec<String>,
+ 1 struct MockMessenger {
+ 2 sent_messages: Vec<String>,
}
impl MockMessenger {
- [3] fn new() -> MockMessenger {
+ 3 fn new() -> MockMessenger {
MockMessenger {
sent_messages: vec![],
}
}
}
- [4] impl Messenger for MockMessenger {
+ 4 impl Messenger for MockMessenger {
fn send(&self, message: &str) {
- [5] self.sent_messages.push(String::from(message));
+ 5 self.sent_messages.push(String::from(message));
}
}
#[test]
- [6] fn it_sends_an_over_75_percent_warning_message() {
+ 6 fn it_sends_an_over_75_percent_warning_message() {
let mock_messenger = MockMessenger::new();
- let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);
+ let mut limit_tracker = LimitTracker::new(
+ &mock_messenger,
+ 100
+ );
limit_tracker.set_value(80);
@@ -1329,37 +1373,40 @@ we can give a `MockMessenger` to a `LimitTracker`. In the definition of the
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 [6].
-First, we create a new `MockMessenger`, which will start with an empty list of
+`value` to something that is more than 75 percent of the `max` value [6]. 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.
+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.
However, there’s one problem with this test, as shown here:
```
-error[E0596]: cannot borrow `self.sent_messages` as mutable, as it is behind a `&` reference
+error[E0596]: cannot borrow `self.sent_messages` as mutable, as it is behind a
+`&` reference
--> src/lib.rs:58:13
|
2 | fn send(&self, msg: &str);
- | ----- help: consider changing that to be a mutable reference: `&mut self`
+ | ----- help: consider changing that to be a mutable reference:
+`&mut self`
...
58 | self.sent_messages.push(String::from(message));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self` is a
+`&` reference, so the data it refers to cannot be borrowed as mutable
```
-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` instead, because then the
+suggestion from the error text to use `&mut self` instead because then the
signature of `send` wouldn’t match the signature in the `Messenger` trait
-definition (feel free to try and see what error message you get).
+definition (feel free to try it and see what error message you get).
This is a situation in which interior mutability can help! We’ll store the
-`sent_messages` within a `RefCell<T>`, 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:
+`sent_messages` within a `RefCell<T>`, 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.
Filename: src/lib.rs
@@ -1370,28 +1417,33 @@ mod tests {
use std::cell::RefCell;
struct MockMessenger {
- [1] sent_messages: RefCell<Vec<String>>,
+ 1 sent_messages: RefCell<Vec<String>>,
}
impl MockMessenger {
fn new() -> MockMessenger {
MockMessenger {
- sent_messages: RefCell::new(vec![]) [2],
+ 2 sent_messages: RefCell::new(vec![]),
}
}
}
impl Messenger for MockMessenger {
fn send(&self, message: &str) {
- [3] self.sent_messages.borrow_mut().push(String::from(message));
+ self.sent_messages
+ 3 .borrow_mut()
+ .push(String::from(message));
}
}
#[test]
fn it_sends_an_over_75_percent_warning_message() {
- // --snip--
+ --snip--
- [4] assert_eq!(mock_messenger.sent_messages.borrow().len(), 1);
+ assert_eq!(
+ 4 mock_messenger.sent_messages.borrow().len(),
+ 1
+ );
}
}
```
@@ -1406,9 +1458,9 @@ instance around the empty vector [2].
For the implementation of the `send` method, the first parameter is still an
immutable borrow of `self`, which matches the trait definition. We call
`borrow_mut` on the `RefCell<Vec<String>>` in `self.sent_messages` [3] to get a
-mutable reference to the value inside the `RefCell<Vec<String>>`, which is
-the vector. Then we can call `push` on the mutable reference to the vector to
-keep track of the messages sent during the test.
+mutable reference to the value inside the `RefCell<Vec<String>>`, which is the
+vector. Then we can call `push` on the mutable reference to the vector to keep
+track of the messages sent during the test.
The last change we have to make is in the assertion: to see how many items are
in the inner vector, we call `borrow` on the `RefCell<Vec<String>>` to get an
@@ -1416,7 +1468,7 @@ immutable reference to the vector [4].
Now that you’ve seen how to use `RefCell<T>`, let’s dig into how it works!
-#### Keeping Track of Borrows at Runtime with `RefCell<T>`
+#### Keeping Track of Borrows at Runtime with RefCell<T>
When creating immutable and mutable references, we use the `&` and `&mut`
syntax, respectively. With `RefCell<T>`, we use the `borrow` and `borrow_mut`
@@ -1428,7 +1480,7 @@ can treat them like regular references.
The `RefCell<T>` keeps track of how many `Ref<T>` and `RefMut<T>` smart
pointers are currently active. Every time we call `borrow`, the `RefCell<T>`
increases its count of how many immutable borrows are active. When a `Ref<T>`
-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<T>` lets us have many immutable
borrows or one mutable borrow at any point in time.
@@ -1442,15 +1494,15 @@ at runtime.
Filename: src/lib.rs
```
- impl Messenger for MockMessenger {
- fn send(&self, message: &str) {
- let mut one_borrow = self.sent_messages.borrow_mut();
- let mut two_borrow = self.sent_messages.borrow_mut();
+impl Messenger for MockMessenger {
+ fn send(&self, message: &str) {
+ let mut one_borrow = self.sent_messages.borrow_mut();
+ let mut two_borrow = self.sent_messages.borrow_mut();
- one_borrow.push(String::from(message));
- two_borrow.push(String::from(message));
- }
+ one_borrow.push(String::from(message));
+ two_borrow.push(String::from(message));
}
+}
```
Listing 15-23: Creating two mutable references in the same scope to see that
@@ -1473,7 +1525,7 @@ BorrowMutError`. This is how `RefCell<T>` handles violations of the borrowing
rules at runtime.
Choosing to catch borrowing errors at runtime rather than compile time, as
-we've done here, means you'd potentially be finding mistakes in your code later
+we’ve done here, means you’d potentially be finding mistakes in your code later
in the development process: possibly not until your code was deployed to
production. Also, your code would incur a small runtime performance penalty as
a result of keeping track of the borrows at runtime rather than compile time.
@@ -1483,7 +1535,7 @@ in a context where only immutable values are allowed. You can use `RefCell<T>`
despite its trade-offs to get more functionality than regular references
provide.
-### Having Multiple Owners of Mutable Data by Combining `Rc<T>` and `RefCell<T>`
+### Allowing Multiple Owners of Mutable Data with Rc<T> and RefCell<T>
A common way to use `RefCell<T>` is in combination with `Rc<T>`. Recall that
`Rc<T>` lets you have multiple owners of some data, but it only gives immutable
@@ -1493,10 +1545,10 @@ 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<T>` to allow multiple lists to share ownership of another list. Because
`Rc<T>` 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<T>` to gain the ability to
+list once we’ve created them. Let’s add in `RefCell<T>` for its ability to
change the values in the lists. Listing 15-24 shows that by using a
`RefCell<T>` in the `Cons` definition, we can modify the value stored in all
-the lists:
+the lists.
Filename: src/main.rs
@@ -1512,14 +1564,14 @@ use std::cell::RefCell;
use std::rc::Rc;
fn main() {
- [1] let value = Rc::new(RefCell::new(5));
+ 1 let value = Rc::new(RefCell::new(5));
- [2] let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));
+ 2 let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));
let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a));
let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a));
- [3] *value.borrow_mut() += 10;
+ 3 *value.borrow_mut() += 10;
println!("a after = {:?}", a);
println!("b after = {:?}", b);
@@ -1541,13 +1593,13 @@ 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` [3]. We do this by calling `borrow_mut` on `value`, which uses
-the automatic dereferencing feature we discussed in Chapter 5 (see the section
-“Where’s the `->` Operator?”) to dereference the `Rc<T>` to the inner
-`RefCell<T>` value. The `borrow_mut` method returns a `RefMut<T>` smart
-pointer, and we use the dereference operator on it and change the inner value.
+the automatic dereferencing feature we discussed in “Where’s the -> Operator?”
+on page XX to dereference the `Rc<T>` to the inner `RefCell<T>` value. The
+`borrow_mut` method returns a `RefMut<T>` 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`:
```
a after = Cons(RefCell { value: 15 }, Nil)
@@ -1561,7 +1613,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<T>` does not work for multithreaded code!
-`Mutex<T>` is the thread-safe version of `RefCell<T>` and we’ll discuss
+`Mutex<T>` is the thread-safe version of `RefCell<T>`, and we’ll discuss
`Mutex<T>` in Chapter 16.
## Reference Cycles Can Leak Memory
@@ -1579,7 +1631,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.
Filename: src/main.rs
@@ -1590,12 +1642,12 @@ use std::rc::Rc;
#[derive(Debug)]
enum List {
- [1] Cons(i32, RefCell<Rc<List>>),
+ 1 Cons(i32, RefCell<Rc<List>>),
Nil,
}
impl List {
- [2] fn tail(&self) -> Option<&RefCell<Rc<List>>> {
+ 2 fn tail(&self) -> Option<&RefCell<Rc<List>>> {
match self {
Cons(_, item) => Some(item),
Nil => None,
@@ -1610,9 +1662,9 @@ modify what a `Cons` variant is referring to
We’re using another variation of the `List` definition from Listing 15-5. The
second element in the `Cons` variant is now `RefCell<Rc<List>>` [1], meaning
that instead of having the ability to modify the `i32` value as we did in
-Listing 15-24, we want to modify the `List` value a `Cons` variant is
-pointing to. We’re also adding a `tail` method [2] to make it convenient for us
-to access the second item if we have a `Cons` variant.
+Listing 15-24, we want to modify the `List` value a `Cons` variant is pointing
+to. We’re also adding a `tail` method [2] to make it convenient for us to
+access the second item if we have a `Cons` variant.
In Listing 15-26, we’re adding a `main` function that uses the definitions in
Listing 15-25. This code creates a list in `a` and a list in `b` that points to
@@ -1624,23 +1676,32 @@ Filename: src/main.rs
```
fn main() {
- [1] let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil))));
+ 1 let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil))));
println!("a initial rc count = {}", Rc::strong_count(&a));
println!("a next item = {:?}", a.tail());
- [2] let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a))));
+ 2 let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a))));
- println!("a rc count after b creation = {}", Rc::strong_count(&a));
+ println!(
+ "a rc count after b creation = {}",
+ Rc::strong_count(&a)
+ );
println!("b initial rc count = {}", Rc::strong_count(&b));
println!("b next item = {:?}", b.tail());
- [3] if let Some(link) = a.tail() {
- [4] *link.borrow_mut() = Rc::clone(&b);
+ 3 if let Some(link) = a.tail() {
+ 4 *link.borrow_mut() = Rc::clone(&b);
}
- println!("b rc count after changing a = {}", Rc::strong_count(&b));
- println!("a rc count after changing a = {}", Rc::strong_count(&a));
+ println!(
+ "b rc count after changing a = {}",
+ Rc::strong_count(&b)
+ );
+ println!(
+ "a rc count after changing a = {}",
+ Rc::strong_count(&a)
+ );
// Uncomment the next line to see that we have a cycle;
// it will overflow the stack
@@ -1653,8 +1714,8 @@ other
We create an `Rc<List>` instance holding a `List` value in the variable `a`
with an initial list of `5, Nil` [1]. We then create an `Rc<List>` instance
-holding another `List` value in the variable `b` that contains the value 10 and
-points to the list in `a` [2].
+holding another `List` value in the variable `b` that contains the value `10`
+and points to the list in `a` [2].
We modify `a` so it points to `b` instead of `Nil`, creating a cycle. We do
that by using the `tail` method to get a reference to the `RefCell<Rc<List>>`
@@ -1675,30 +1736,28 @@ b rc count after changing a = 2
a rc count after changing a = 2
```
-The reference count of the `Rc<List>` instances in both `a` and `b` are 2 after
+The reference count of the `Rc<List>` 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<List>` instance
-from 2 to 1. The memory that `Rc<List>` has on the heap won’t be dropped at
-this point, because its reference count is 1, not 0. Then Rust drops `a`, which
-decreases the reference count of the `a` `Rc<List>` instance from 2 to 1 as
-well. This instance’s memory can’t be dropped either, because the other
+variable `b`, which decreases the reference count of the `b` `Rc<List>`
+instance from 2 to 1. The memory that `Rc<List>` has on the heap won’t be
+dropped at this point because its reference count is 1, not 0. Then Rust drops
+`a`, which decreases the reference count of the `a` `Rc<List>` instance from 2
+to 1 as well. This instance’s memory can’t be dropped either, because the other
`Rc<List>` instance still refers to it. The memory allocated to the list will
remain uncollected forever. To visualize this reference cycle, we’ve created a
diagram in Figure 15-4.
-<img alt="Reference cycle of lists" src="img/trpl15-04.svg" class="center" />
-
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.
-Compared to a real-world program, the consequences creating a reference cycle
-in this example aren’t very dire: right after we create the reference cycle,
-the program ends. However, if a more complex program allocated lots of memory
-in a cycle and held onto it for a long time, the program would use more memory
-than it needed and might overwhelm the system, causing it to run out of
+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, the program ends. However, if a more complex program allocated lots of
+memory in a cycle and held onto it for a long time, the program would use more
+memory than it needed and might overwhelm the system, causing it to run out of
available memory.
Creating reference cycles is not easily done, but it’s not impossible either.
@@ -1719,7 +1778,7 @@ 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<T>` into a `Weak<T>`
+### Preventing Reference Cycles Using Weak<T>
So far, we’ve demonstrated that calling `Rc::clone` increases the
`strong_count` of an `Rc<T>` instance, and an `Rc<T>` instance is only cleaned
@@ -1727,7 +1786,7 @@ up if its `strong_count` is 0. You can also create a *weak reference* to the
value within an `Rc<T>` instance by calling `Rc::downgrade` and passing a
reference to the `Rc<T>`. Strong references are how you can share ownership of
an `Rc<T>` instance. Weak references don’t express an ownership relationship,
-and their count doesn't affect when an `Rc<T>` instance is cleaned up. They
+and their count doesn’t affect when an `Rc<T>` 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.
@@ -1739,7 +1798,7 @@ Instead of increasing the `strong_count` in the `Rc<T>` instance by 1, calling
`Rc<T>` instance to be cleaned up.
Because the value that `Weak<T>` references might have been dropped, to do
-anything with the value that a `Weak<T>` is pointing to, you must make sure the
+anything with the value that a `Weak<T>` is pointing to you must make sure the
value still exists. Do this by calling the `upgrade` method on a `Weak<T>`
instance, which will return an `Option<Rc<T>>`. You’ll get a result of `Some`
if the `Rc<T>` value has not been dropped yet and a result of `None` if the
@@ -1751,7 +1810,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
@@ -1777,8 +1836,8 @@ modify which nodes are children of another node, so we have a `RefCell<T>` in
`children` around the `Vec<Rc<Node>>`.
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.
Filename: src/main.rs
@@ -1810,7 +1869,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<T>`, because that would
+`parent` should be. We know it can’t contain an `Rc<T>` 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.
@@ -1820,7 +1879,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<T>`, we’ll make the type of `parent` use `Weak<T>`,
+So, instead of `Rc<T>`, we’ll make the type of `parent` use `Weak<T>`,
specifically a `RefCell<Weak<Node>>`. Now our `Node` struct definition looks
like this:
@@ -1838,35 +1897,41 @@ 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`:
+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`.
-Filename: src/main.rs
+Filename: src/main.rs
```
fn main() {
let leaf = Rc::new(Node {
value: 3,
- [1] parent: RefCell::new(Weak::new()),
+ 1 parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![]),
});
- [2] println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
+ 2 println!(
+ "leaf parent = {:?}",
+ leaf.parent.borrow().upgrade()
+ );
let branch = Rc::new(Node {
value: 5,
- [3] parent: RefCell::new(Weak::new()),
+ 3 parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![Rc::clone(&leaf)]),
});
- [4] *leaf.parent.borrow_mut() = Rc::downgrade(&branch);
+ 4 *leaf.parent.borrow_mut() = Rc::downgrade(&branch);
- [5] println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
+ 5 println!(
+ "leaf parent = {:?}",
+ leaf.parent.borrow().upgrade()
+ );
}
```
-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,
@@ -1881,13 +1946,13 @@ leaf parent = None
```
When we create the `branch` node, it will also have a new `Weak<Node>`
-reference in the `parent` field [3], because `branch` doesn’t have a parent
+reference in the `parent` field [3] 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<Node>`
reference to its parent [4]. We use the `borrow_mut` method on the
`RefCell<Weak<Node>>` in the `parent` field of `leaf`, and then we use the
`Rc::downgrade` function to create a `Weak<Node>` reference to `branch` from
-the `Rc<Node>` in `branch.`
+the `Rc<Node>` in `branch`.
When we print the parent of `leaf` again [5], this time we’ll get a `Some`
variant holding `branch`: now `leaf` can access its parent! When we print
@@ -1904,13 +1969,13 @@ The lack of infinite output indicates that this code didn’t create a reference
cycle. We can also tell this by looking at the values we get from calling
`Rc::strong_count` and `Rc::weak_count`.
-#### Visualizing Changes to `strong_count` and `weak_count`
+#### Visualizing Changes to strong_count and weak_count
Let’s look at how the `strong_count` and `weak_count` values of the `Rc<Node>`
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.
Filename: src/main.rs
@@ -1922,13 +1987,13 @@ fn main() {
children: RefCell::new(vec![]),
});
- [1] println!(
+ 1 println!(
"leaf strong = {}, weak = {}",
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
);
- [2] {
+ 2 {
let branch = Rc::new(Node {
value: 5,
parent: RefCell::new(Weak::new()),
@@ -1937,21 +2002,24 @@ fn main() {
*leaf.parent.borrow_mut() = Rc::downgrade(&branch);
- [3] println!(
+ 3 println!(
"branch strong = {}, weak = {}",
Rc::strong_count(&branch),
Rc::weak_count(&branch),
);
- [4] println!(
+ 4 println!(
"leaf strong = {}, weak = {}",
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
);
- [5] }
+ 5 }
- [6] println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
- [7] println!(
+ 6 println!(
+ "leaf parent = {:?}",
+ leaf.parent.borrow().upgrade()
+ );
+ 7 println!(
"leaf strong = {}, weak = {}",
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
@@ -1967,7 +2035,7 @@ count of 0 [1]. In the inner scope [2], we create `branch` and associate it
with `leaf`, at which point when we print the counts [3], the `Rc<Node>` in
`branch` will have a strong count of 1 and a weak count of 1 (for `leaf.parent`
pointing to `branch` with a `Weak<Node>`). When we print the counts in `leaf`
-[4], we’ll see it will have a strong count of 2, because `branch` now has a
+[4], we’ll see it will have a strong count of 2 because `branch` now has a
clone of the `Rc<Node>` of `leaf` stored in `branch.children`, but will still
have a weak count of 0.
@@ -1978,7 +2046,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 [6]. At the end of the program [7], the `Rc<Node>` in `leaf` has a
-strong count of 1 and a weak count of 0, because the variable `leaf` is now the
+strong count of 1 and a weak count of 0 because the variable `leaf` is now the
only reference to the `Rc<Node>` again.
All of the logic that manages the counts and value dropping is built into
@@ -2005,7 +2073,8 @@ memory leaks and how to prevent them using `Weak<T>`.
If this chapter has piqued your interest and you want to implement your own
smart pointers, check out “The Rustonomicon” at
-*https://doc.rust-lang.org/stable/nomicon/* for more useful information.
+*https://doc.rust-lang.org/stable/nomicon* for more useful information.
Next, we’ll talk about concurrency in Rust. You’ll even learn about a few new
smart pointers.
+