]>
Commit | Line | Data |
---|---|---|
13cf67c4 XL |
1 | ## `RefCell<T>` and the Interior Mutability Pattern |
2 | ||
13cf67c4 XL |
3 | *Interior mutability* is a design pattern in Rust that allows you to mutate |
4 | data even when there are immutable references to that data; normally, this | |
5 | action is disallowed by the borrowing rules. To mutate data, the pattern uses | |
6 | `unsafe` code inside a data structure to bend Rust’s usual rules that govern | |
e74abb32 XL |
7 | mutation and borrowing. We haven’t yet covered unsafe code; we will in Chapter |
8 | 19. We can use types that use the interior mutability pattern when we can | |
9 | ensure that the borrowing rules will be followed at runtime, even though the | |
10 | compiler can’t guarantee that. The `unsafe` code involved is then wrapped in a | |
11 | safe API, and the outer type is still immutable. | |
13cf67c4 XL |
12 | |
13 | Let’s explore this concept by looking at the `RefCell<T>` type that follows the | |
14 | interior mutability pattern. | |
15 | ||
16 | ### Enforcing Borrowing Rules at Runtime with `RefCell<T>` | |
17 | ||
18 | Unlike `Rc<T>`, the `RefCell<T>` type represents single ownership over the data | |
19 | it holds. So, what makes `RefCell<T>` different from a type like `Box<T>`? | |
20 | Recall the borrowing rules you learned in Chapter 4: | |
21 | ||
22 | * At any given time, you can have *either* (but not both of) one mutable | |
23 | reference or any number of immutable references. | |
24 | * References must always be valid. | |
25 | ||
26 | With references and `Box<T>`, the borrowing rules’ invariants are enforced at | |
27 | compile time. With `RefCell<T>`, these invariants are enforced *at runtime*. | |
28 | With references, if you break these rules, you’ll get a compiler error. With | |
29 | `RefCell<T>`, if you break these rules, your program will panic and exit. | |
30 | ||
31 | The advantages of checking the borrowing rules at compile time are that errors | |
32 | will be caught sooner in the development process, and there is no impact on | |
33 | runtime performance because all the analysis is completed beforehand. For those | |
34 | reasons, checking the borrowing rules at compile time is the best choice in the | |
35 | majority of cases, which is why this is Rust’s default. | |
36 | ||
37 | The advantage of checking the borrowing rules at runtime instead is that | |
38 | certain memory-safe scenarios are then allowed, whereas they are disallowed by | |
39 | the compile-time checks. Static analysis, like the Rust compiler, is inherently | |
40 | conservative. Some properties of code are impossible to detect by analyzing the | |
41 | code: the most famous example is the Halting Problem, which is beyond the scope | |
42 | of this book but is an interesting topic to research. | |
43 | ||
44 | Because some analysis is impossible, if the Rust compiler can’t be sure the | |
45 | code complies with the ownership rules, it might reject a correct program; in | |
46 | this way, it’s conservative. If Rust accepted an incorrect program, users | |
47 | wouldn’t be able to trust in the guarantees Rust makes. However, if Rust | |
48 | rejects a correct program, the programmer will be inconvenienced, but nothing | |
49 | catastrophic can occur. The `RefCell<T>` type is useful when you’re sure your | |
50 | code follows the borrowing rules but the compiler is unable to understand and | |
51 | guarantee that. | |
52 | ||
53 | Similar to `Rc<T>`, `RefCell<T>` is only for use in single-threaded scenarios | |
54 | and will give you a compile-time error if you try using it in a multithreaded | |
55 | context. We’ll talk about how to get the functionality of `RefCell<T>` in a | |
56 | multithreaded program in Chapter 16. | |
57 | ||
58 | Here is a recap of the reasons to choose `Box<T>`, `Rc<T>`, or `RefCell<T>`: | |
59 | ||
60 | * `Rc<T>` enables multiple owners of the same data; `Box<T>` and `RefCell<T>` | |
61 | have single owners. | |
62 | * `Box<T>` allows immutable or mutable borrows checked at compile time; `Rc<T>` | |
63 | allows only immutable borrows checked at compile time; `RefCell<T>` allows | |
64 | immutable or mutable borrows checked at runtime. | |
65 | * Because `RefCell<T>` allows mutable borrows checked at runtime, you can | |
66 | mutate the value inside the `RefCell<T>` even when the `RefCell<T>` is | |
67 | immutable. | |
68 | ||
69 | Mutating the value inside an immutable value is the *interior mutability* | |
70 | pattern. Let’s look at a situation in which interior mutability is useful and | |
71 | examine how it’s possible. | |
72 | ||
73 | ### Interior Mutability: A Mutable Borrow to an Immutable Value | |
74 | ||
75 | A consequence of the borrowing rules is that when you have an immutable value, | |
76 | you can’t borrow it mutably. For example, this code won’t compile: | |
77 | ||
78 | ```rust,ignore,does_not_compile | |
74b04a01 | 79 | {{#rustdoc_include ../listings/ch15-smart-pointers/no-listing-01-cant-borrow-immutable-as-mutable/src/main.rs}} |
13cf67c4 XL |
80 | ``` |
81 | ||
82 | If you tried to compile this code, you’d get the following error: | |
83 | ||
f035d41b | 84 | ```console |
74b04a01 | 85 | {{#include ../listings/ch15-smart-pointers/no-listing-01-cant-borrow-immutable-as-mutable/output.txt}} |
13cf67c4 XL |
86 | ``` |
87 | ||
88 | However, there are situations in which it would be useful for a value to mutate | |
89 | itself in its methods but appear immutable to other code. Code outside the | |
90 | value’s methods would not be able to mutate the value. Using `RefCell<T>` is | |
91 | one way to get the ability to have interior mutability. But `RefCell<T>` | |
92 | doesn’t get around the borrowing rules completely: the borrow checker in the | |
93 | compiler allows this interior mutability, and the borrowing rules are checked | |
94 | at runtime instead. If you violate the rules, you’ll get a `panic!` instead of | |
95 | a compiler error. | |
96 | ||
97 | Let’s work through a practical example where we can use `RefCell<T>` to mutate | |
98 | an immutable value and see why that is useful. | |
99 | ||
100 | #### A Use Case for Interior Mutability: Mock Objects | |
101 | ||
102 | A *test double* is the general programming concept for a type used in place of | |
103 | another type during testing. *Mock objects* are specific types of test doubles | |
104 | that record what happens during a test so you can assert that the correct | |
105 | actions took place. | |
106 | ||
107 | Rust doesn’t have objects in the same sense as other languages have objects, | |
108 | and Rust doesn’t have mock object functionality built into the standard library | |
109 | as some other languages do. However, you can definitely create a struct that | |
110 | will serve the same purposes as a mock object. | |
111 | ||
112 | Here’s the scenario we’ll test: we’ll create a library that tracks a value | |
113 | against a maximum value and sends messages based on how close to the maximum | |
114 | value the current value is. This library could be used to keep track of a | |
115 | user’s quota for the number of API calls they’re allowed to make, for example. | |
116 | ||
117 | Our library will only provide the functionality of tracking how close to the | |
118 | maximum a value is and what the messages should be at what times. Applications | |
119 | that use our library will be expected to provide the mechanism for sending the | |
120 | messages: the application could put a message in the application, send an | |
121 | email, send a text message, or something else. The library doesn’t need to know | |
122 | that detail. All it needs is something that implements a trait we’ll provide | |
123 | called `Messenger`. Listing 15-20 shows the library code: | |
124 | ||
125 | <span class="filename">Filename: src/lib.rs</span> | |
126 | ||
5869c6ff | 127 | ```rust,noplayground |
74b04a01 | 128 | {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-20/src/lib.rs}} |
13cf67c4 XL |
129 | ``` |
130 | ||
131 | <span class="caption">Listing 15-20: A library to keep track of how close a | |
132 | value is to a maximum value and warn when the value is at certain levels</span> | |
133 | ||
134 | One important part of this code is that the `Messenger` trait has one method | |
135 | called `send` that takes an immutable reference to `self` and the text of the | |
6a06907d XL |
136 | message. This trait is the interface our mock object needs to implement so that |
137 | the mock can be used in the same way a real object is. The other important part | |
138 | is that we want to test the behavior of the `set_value` method on the | |
139 | `LimitTracker`. We can change what we pass in for the `value` parameter, but | |
140 | `set_value` doesn’t return anything for us to make assertions on. We want to be | |
141 | able to say that if we create a `LimitTracker` with something that implements | |
142 | the `Messenger` trait and a particular value for `max`, when we pass different | |
143 | numbers for `value`, the messenger is told to send the appropriate messages. | |
13cf67c4 XL |
144 | |
145 | We need a mock object that, instead of sending an email or text message when we | |
146 | call `send`, will only keep track of the messages it’s told to send. We can | |
147 | create a new instance of the mock object, create a `LimitTracker` that uses the | |
148 | mock object, call the `set_value` method on `LimitTracker`, and then check that | |
149 | the mock object has the messages we expect. Listing 15-21 shows an attempt to | |
150 | implement a mock object to do just that, but the borrow checker won’t allow it: | |
151 | ||
152 | <span class="filename">Filename: src/lib.rs</span> | |
153 | ||
48663c56 | 154 | ```rust,ignore,does_not_compile |
74b04a01 | 155 | {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-21/src/lib.rs:here}} |
13cf67c4 XL |
156 | ``` |
157 | ||
158 | <span class="caption">Listing 15-21: An attempt to implement a `MockMessenger` | |
159 | that isn’t allowed by the borrow checker</span> | |
160 | ||
161 | This test code defines a `MockMessenger` struct that has a `sent_messages` | |
162 | field with a `Vec` of `String` values to keep track of the messages it’s told | |
163 | to send. We also define an associated function `new` to make it convenient to | |
164 | create new `MockMessenger` values that start with an empty list of messages. We | |
165 | then implement the `Messenger` trait for `MockMessenger` so we can give a | |
166 | `MockMessenger` to a `LimitTracker`. In the definition of the `send` method, we | |
167 | take the message passed in as a parameter and store it in the `MockMessenger` | |
168 | list of `sent_messages`. | |
169 | ||
170 | In the test, we’re testing what happens when the `LimitTracker` is told to set | |
171 | `value` to something that is more than 75 percent of the `max` value. First, we | |
172 | create a new `MockMessenger`, which will start with an empty list of messages. | |
173 | Then we create a new `LimitTracker` and give it a reference to the new | |
174 | `MockMessenger` and a `max` value of 100. We call the `set_value` method on the | |
175 | `LimitTracker` with a value of 80, which is more than 75 percent of 100. Then | |
176 | we assert that the list of messages that the `MockMessenger` is keeping track | |
177 | of should now have one message in it. | |
178 | ||
179 | However, there’s one problem with this test, as shown here: | |
180 | ||
6a06907d | 181 | ```console |
74b04a01 | 182 | {{#include ../listings/ch15-smart-pointers/listing-15-21/output.txt}} |
13cf67c4 XL |
183 | ``` |
184 | ||
185 | We can’t modify the `MockMessenger` to keep track of the messages, because the | |
186 | `send` method takes an immutable reference to `self`. We also can’t take the | |
187 | suggestion from the error text to use `&mut self` instead, because then the | |
188 | signature of `send` wouldn’t match the signature in the `Messenger` trait | |
189 | definition (feel free to try and see what error message you get). | |
190 | ||
191 | This is a situation in which interior mutability can help! We’ll store the | |
6a06907d | 192 | `sent_messages` within a `RefCell<T>`, and then the `send` method will be |
13cf67c4 XL |
193 | able to modify `sent_messages` to store the messages we’ve seen. Listing 15-22 |
194 | shows what that looks like: | |
195 | ||
196 | <span class="filename">Filename: src/lib.rs</span> | |
197 | ||
fc512014 | 198 | ```rust,noplayground |
74b04a01 | 199 | {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-22/src/lib.rs:here}} |
13cf67c4 XL |
200 | ``` |
201 | ||
202 | <span class="caption">Listing 15-22: Using `RefCell<T>` to mutate an inner | |
203 | value while the outer value is considered immutable</span> | |
204 | ||
205 | The `sent_messages` field is now of type `RefCell<Vec<String>>` instead of | |
206 | `Vec<String>`. In the `new` function, we create a new `RefCell<Vec<String>>` | |
207 | instance around the empty vector. | |
208 | ||
209 | For the implementation of the `send` method, the first parameter is still an | |
210 | immutable borrow of `self`, which matches the trait definition. We call | |
211 | `borrow_mut` on the `RefCell<Vec<String>>` in `self.sent_messages` to get a | |
212 | mutable reference to the value inside the `RefCell<Vec<String>>`, which is | |
213 | the vector. Then we can call `push` on the mutable reference to the vector to | |
214 | keep track of the messages sent during the test. | |
215 | ||
216 | The last change we have to make is in the assertion: to see how many items are | |
217 | in the inner vector, we call `borrow` on the `RefCell<Vec<String>>` to get an | |
218 | immutable reference to the vector. | |
219 | ||
220 | Now that you’ve seen how to use `RefCell<T>`, let’s dig into how it works! | |
221 | ||
222 | #### Keeping Track of Borrows at Runtime with `RefCell<T>` | |
223 | ||
224 | When creating immutable and mutable references, we use the `&` and `&mut` | |
225 | syntax, respectively. With `RefCell<T>`, we use the `borrow` and `borrow_mut` | |
226 | methods, which are part of the safe API that belongs to `RefCell<T>`. The | |
227 | `borrow` method returns the smart pointer type `Ref<T>`, and `borrow_mut` | |
228 | returns the smart pointer type `RefMut<T>`. Both types implement `Deref`, so we | |
229 | can treat them like regular references. | |
230 | ||
231 | The `RefCell<T>` keeps track of how many `Ref<T>` and `RefMut<T>` smart | |
232 | pointers are currently active. Every time we call `borrow`, the `RefCell<T>` | |
233 | increases its count of how many immutable borrows are active. When a `Ref<T>` | |
234 | value goes out of scope, the count of immutable borrows goes down by one. Just | |
235 | like the compile-time borrowing rules, `RefCell<T>` lets us have many immutable | |
236 | borrows or one mutable borrow at any point in time. | |
237 | ||
238 | If we try to violate these rules, rather than getting a compiler error as we | |
239 | would with references, the implementation of `RefCell<T>` will panic at | |
240 | runtime. Listing 15-23 shows a modification of the implementation of `send` in | |
241 | Listing 15-22. We’re deliberately trying to create two mutable borrows active | |
242 | for the same scope to illustrate that `RefCell<T>` prevents us from doing this | |
243 | at runtime. | |
244 | ||
245 | <span class="filename">Filename: src/lib.rs</span> | |
246 | ||
247 | ```rust,ignore,panics | |
74b04a01 | 248 | {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-23/src/lib.rs:here}} |
13cf67c4 XL |
249 | ``` |
250 | ||
251 | <span class="caption">Listing 15-23: Creating two mutable references in the | |
252 | same scope to see that `RefCell<T>` will panic</span> | |
253 | ||
254 | We create a variable `one_borrow` for the `RefMut<T>` smart pointer returned | |
255 | from `borrow_mut`. Then we create another mutable borrow in the same way in the | |
256 | variable `two_borrow`. This makes two mutable references in the same scope, | |
257 | which isn’t allowed. When we run the tests for our library, the code in Listing | |
258 | 15-23 will compile without any errors, but the test will fail: | |
259 | ||
f035d41b | 260 | ```console |
74b04a01 | 261 | {{#include ../listings/ch15-smart-pointers/listing-15-23/output.txt}} |
13cf67c4 XL |
262 | ``` |
263 | ||
264 | Notice that the code panicked with the message `already borrowed: | |
265 | BorrowMutError`. This is how `RefCell<T>` handles violations of the borrowing | |
266 | rules at runtime. | |
267 | ||
268 | Catching borrowing errors at runtime rather than compile time means that you | |
269 | would find a mistake in your code later in the development process and possibly | |
270 | not until your code was deployed to production. Also, your code would incur a | |
271 | small runtime performance penalty as a result of keeping track of the borrows | |
272 | at runtime rather than compile time. However, using `RefCell<T>` makes it | |
273 | possible to write a mock object that can modify itself to keep track of the | |
274 | messages it has seen while you’re using it in a context where only immutable | |
275 | values are allowed. You can use `RefCell<T>` despite its trade-offs to get more | |
276 | functionality than regular references provide. | |
277 | ||
278 | ### Having Multiple Owners of Mutable Data by Combining `Rc<T>` and `RefCell<T>` | |
279 | ||
280 | A common way to use `RefCell<T>` is in combination with `Rc<T>`. Recall that | |
281 | `Rc<T>` lets you have multiple owners of some data, but it only gives immutable | |
282 | access to that data. If you have an `Rc<T>` that holds a `RefCell<T>`, you can | |
283 | get a value that can have multiple owners *and* that you can mutate! | |
284 | ||
285 | For example, recall the cons list example in Listing 15-18 where we used | |
286 | `Rc<T>` to allow multiple lists to share ownership of another list. Because | |
287 | `Rc<T>` holds only immutable values, we can’t change any of the values in the | |
288 | list once we’ve created them. Let’s add in `RefCell<T>` to gain the ability to | |
289 | change the values in the lists. Listing 15-24 shows that by using a | |
290 | `RefCell<T>` in the `Cons` definition, we can modify the value stored in all | |
291 | the lists: | |
292 | ||
293 | <span class="filename">Filename: src/main.rs</span> | |
294 | ||
295 | ```rust | |
74b04a01 | 296 | {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-24/src/main.rs}} |
13cf67c4 XL |
297 | ``` |
298 | ||
299 | <span class="caption">Listing 15-24: Using `Rc<RefCell<i32>>` to create a | |
300 | `List` that we can mutate</span> | |
301 | ||
302 | We create a value that is an instance of `Rc<RefCell<i32>>` and store it in a | |
303 | variable named `value` so we can access it directly later. Then we create a | |
304 | `List` in `a` with a `Cons` variant that holds `value`. We need to clone | |
305 | `value` so both `a` and `value` have ownership of the inner `5` value rather | |
306 | than transferring ownership from `value` to `a` or having `a` borrow from | |
307 | `value`. | |
308 | ||
309 | We wrap the list `a` in an `Rc<T>` so when we create lists `b` and `c`, they | |
310 | can both refer to `a`, which is what we did in Listing 15-18. | |
311 | ||
312 | After we’ve created the lists in `a`, `b`, and `c`, we add 10 to the value in | |
313 | `value`. We do this by calling `borrow_mut` on `value`, which uses the | |
314 | automatic dereferencing feature we discussed in Chapter 5 (see the section | |
9fa01778 XL |
315 | [“Where’s the `->` Operator?”][wheres-the---operator]<!-- ignore -->) to |
316 | dereference the `Rc<T>` to the inner `RefCell<T>` value. The `borrow_mut` | |
317 | method returns a `RefMut<T>` smart pointer, and we use the dereference operator | |
318 | on it and change the inner value. | |
13cf67c4 XL |
319 | |
320 | When we print `a`, `b`, and `c`, we can see that they all have the modified | |
321 | value of 15 rather than 5: | |
322 | ||
f035d41b | 323 | ```console |
74b04a01 | 324 | {{#include ../listings/ch15-smart-pointers/listing-15-24/output.txt}} |
13cf67c4 XL |
325 | ``` |
326 | ||
327 | This technique is pretty neat! By using `RefCell<T>`, we have an outwardly | |
328 | immutable `List` value. But we can use the methods on `RefCell<T>` that provide | |
329 | access to its interior mutability so we can modify our data when we need to. | |
330 | The runtime checks of the borrowing rules protect us from data races, and it’s | |
331 | sometimes worth trading a bit of speed for this flexibility in our data | |
332 | structures. | |
333 | ||
334 | The standard library has other types that provide interior mutability, such as | |
335 | `Cell<T>`, which is similar except that instead of giving references to the | |
336 | inner value, the value is copied in and out of the `Cell<T>`. There’s also | |
337 | `Mutex<T>`, which offers interior mutability that’s safe to use across threads; | |
338 | we’ll discuss its use in Chapter 16. Check out the standard library docs for | |
339 | more details on the differences between these types. | |
9fa01778 XL |
340 | |
341 | [wheres-the---operator]: ch05-03-method-syntax.html#wheres-the---operator |