]>
Commit | Line | Data |
---|---|---|
9346a6ac AL |
1 | % References and Borrowing |
2 | ||
b039eaaf | 3 | This guide is two of three presenting Rust’s ownership system. This is one of |
bd371182 AL |
4 | Rust’s most unique and compelling features, with which Rust developers should |
5 | become quite acquainted. Ownership is how Rust achieves its largest goal, | |
6 | memory safety. There are a few distinct concepts, each with its own | |
7 | chapter: | |
8 | ||
9 | * [ownership][ownership], the key concept | |
10 | * borrowing, which you’re reading now | |
11 | * [lifetimes][lifetimes], an advanced concept of borrowing | |
12 | ||
13 | These three chapters are related, and in order. You’ll need all three to fully | |
14 | understand the ownership system. | |
15 | ||
16 | [ownership]: ownership.html | |
17 | [lifetimes]: lifetimes.html | |
18 | ||
19 | # Meta | |
20 | ||
21 | Before we get to the details, two important notes about the ownership system. | |
22 | ||
23 | Rust has a focus on safety and speed. It accomplishes these goals through many | |
24 | ‘zero-cost abstractions’, which means that in Rust, abstractions cost as little | |
25 | as possible in order to make them work. The ownership system is a prime example | |
54a0048b | 26 | of a zero-cost abstraction. All of the analysis we’ll talk about in this guide |
bd371182 AL |
27 | is _done at compile time_. You do not pay any run-time cost for any of these |
28 | features. | |
29 | ||
30 | However, this system does have a certain cost: learning curve. Many new users | |
31 | to Rust experience something we like to call ‘fighting with the borrow | |
32 | checker’, where the Rust compiler refuses to compile a program that the author | |
33 | thinks is valid. This often happens because the programmer’s mental model of | |
34 | how ownership should work doesn’t match the actual rules that Rust implements. | |
35 | You probably will experience similar things at first. There is good news, | |
36 | however: more experienced Rust developers report that once they work with the | |
37 | rules of the ownership system for a period of time, they fight the borrow | |
38 | checker less and less. | |
39 | ||
40 | With that in mind, let’s learn about borrowing. | |
41 | ||
42 | # Borrowing | |
43 | ||
44 | At the end of the [ownership][ownership] section, we had a nasty function that looked | |
45 | like this: | |
46 | ||
47 | ```rust | |
48 | fn foo(v1: Vec<i32>, v2: Vec<i32>) -> (Vec<i32>, Vec<i32>, i32) { | |
49 | // do stuff with v1 and v2 | |
50 | ||
51 | // hand back ownership, and the result of our function | |
52 | (v1, v2, 42) | |
53 | } | |
54 | ||
55 | let v1 = vec![1, 2, 3]; | |
56 | let v2 = vec![1, 2, 3]; | |
57 | ||
58 | let (v1, v2, answer) = foo(v1, v2); | |
59 | ``` | |
60 | ||
61 | This is not idiomatic Rust, however, as it doesn’t take advantage of borrowing. Here’s | |
62 | the first step: | |
63 | ||
64 | ```rust | |
65 | fn foo(v1: &Vec<i32>, v2: &Vec<i32>) -> i32 { | |
66 | // do stuff with v1 and v2 | |
67 | ||
68 | // return the answer | |
69 | 42 | |
70 | } | |
71 | ||
72 | let v1 = vec![1, 2, 3]; | |
73 | let v2 = vec![1, 2, 3]; | |
74 | ||
75 | let answer = foo(&v1, &v2); | |
76 | ||
77 | // we can use v1 and v2 here! | |
78 | ``` | |
79 | ||
80 | Instead of taking `Vec<i32>`s as our arguments, we take a reference: | |
81 | `&Vec<i32>`. And instead of passing `v1` and `v2` directly, we pass `&v1` and | |
82 | `&v2`. We call the `&T` type a ‘reference’, and rather than owning the resource, | |
83 | it borrows ownership. A binding that borrows something does not deallocate the | |
84 | resource when it goes out of scope. This means that after the call to `foo()`, | |
85 | we can use our original bindings again. | |
86 | ||
9cc50fc6 | 87 | References are immutable, like bindings. This means that inside of `foo()`, |
bd371182 AL |
88 | the vectors can’t be changed at all: |
89 | ||
90 | ```rust,ignore | |
91 | fn foo(v: &Vec<i32>) { | |
92 | v.push(5); | |
93 | } | |
94 | ||
95 | let v = vec![]; | |
96 | ||
97 | foo(&v); | |
98 | ``` | |
99 | ||
100 | errors with: | |
101 | ||
102 | ```text | |
103 | error: cannot borrow immutable borrowed content `*v` as mutable | |
104 | v.push(5); | |
105 | ^ | |
106 | ``` | |
107 | ||
108 | Pushing a value mutates the vector, and so we aren’t allowed to do it. | |
109 | ||
110 | # &mut references | |
111 | ||
112 | There’s a second kind of reference: `&mut T`. A ‘mutable reference’ allows you | |
113 | to mutate the resource you’re borrowing. For example: | |
114 | ||
115 | ```rust | |
116 | let mut x = 5; | |
117 | { | |
118 | let y = &mut x; | |
119 | *y += 1; | |
120 | } | |
121 | println!("{}", x); | |
122 | ``` | |
123 | ||
124 | This will print `6`. We make `y` a mutable reference to `x`, then add one to | |
92a42be0 SL |
125 | the thing `y` points at. You’ll notice that `x` had to be marked `mut` as well. |
126 | If it wasn’t, we couldn’t take a mutable borrow to an immutable value. | |
bd371182 | 127 | |
e9174d1e | 128 | You'll also notice we added an asterisk (`*`) in front of `y`, making it `*y`, |
9cc50fc6 | 129 | this is because `y` is a `&mut` reference. You'll also need to use them for |
e9174d1e SL |
130 | accessing the contents of a reference as well. |
131 | ||
9cc50fc6 | 132 | Otherwise, `&mut` references are like references. There _is_ a large |
bd371182 AL |
133 | difference between the two, and how they interact, though. You can tell |
134 | something is fishy in the above example, because we need that extra scope, with | |
135 | the `{` and `}`. If we remove them, we get an error: | |
136 | ||
137 | ```text | |
138 | error: cannot borrow `x` as immutable because it is also borrowed as mutable | |
139 | println!("{}", x); | |
140 | ^ | |
141 | note: previous borrow of `x` occurs here; the mutable borrow prevents | |
142 | subsequent moves, borrows, or modification of `x` until the borrow ends | |
143 | let y = &mut x; | |
144 | ^ | |
145 | note: previous borrow ends here | |
146 | fn main() { | |
147 | ||
148 | } | |
149 | ^ | |
150 | ``` | |
151 | ||
152 | As it turns out, there are rules. | |
153 | ||
154 | # The Rules | |
155 | ||
156 | Here’s the rules about borrowing in Rust: | |
157 | ||
62682a34 SL |
158 | First, any borrow must last for a scope no greater than that of the owner. |
159 | Second, you may have one or the other of these two kinds of borrows, but not | |
160 | both at the same time: | |
bd371182 | 161 | |
e9174d1e SL |
162 | * one or more references (`&T`) to a resource, |
163 | * exactly one mutable reference (`&mut T`). | |
bd371182 AL |
164 | |
165 | ||
54a0048b SL |
166 | You may notice that this is very similar to, though not exactly the same as, |
167 | the definition of a data race: | |
bd371182 AL |
168 | |
169 | > There is a ‘data race’ when two or more pointers access the same memory | |
170 | > location at the same time, where at least one of them is writing, and the | |
171 | > operations are not synchronized. | |
172 | ||
173 | With references, you may have as many as you’d like, since none of them are | |
92a42be0 SL |
174 | writing. However, as we can only have one `&mut` at a time, it is impossible to |
175 | have a data race. This is how Rust prevents data races at compile time: we’ll | |
176 | get errors if we break the rules. | |
bd371182 AL |
177 | |
178 | With this in mind, let’s consider our example again. | |
179 | ||
180 | ## Thinking in scopes | |
181 | ||
182 | Here’s the code: | |
183 | ||
184 | ```rust,ignore | |
185 | let mut x = 5; | |
186 | let y = &mut x; | |
187 | ||
188 | *y += 1; | |
189 | ||
190 | println!("{}", x); | |
191 | ``` | |
192 | ||
193 | This code gives us this error: | |
194 | ||
195 | ```text | |
196 | error: cannot borrow `x` as immutable because it is also borrowed as mutable | |
197 | println!("{}", x); | |
198 | ^ | |
199 | ``` | |
200 | ||
201 | This is because we’ve violated the rules: we have a `&mut T` pointing to `x`, | |
202 | and so we aren’t allowed to create any `&T`s. One or the other. The note | |
203 | hints at how to think about this problem: | |
204 | ||
205 | ```text | |
206 | note: previous borrow ends here | |
207 | fn main() { | |
208 | ||
209 | } | |
210 | ^ | |
211 | ``` | |
212 | ||
62682a34 | 213 | In other words, the mutable borrow is held through the rest of our example. What |
54a0048b SL |
214 | we want is for the mutable borrow by `y` to end so that the resource can be |
215 | returned to the owner, `x`. `x` can then provide a immutable borrow to `println!`. | |
216 | In Rust, borrowing is tied to the scope that the borrow is valid for. And our | |
217 | scopes look like this: | |
bd371182 AL |
218 | |
219 | ```rust,ignore | |
220 | let mut x = 5; | |
221 | ||
222 | let y = &mut x; // -+ &mut borrow of x starts here | |
223 | // | | |
224 | *y += 1; // | | |
225 | // | | |
226 | println!("{}", x); // -+ - try to borrow x here | |
227 | // -+ &mut borrow of x ends here | |
228 | ``` | |
229 | ||
230 | The scopes conflict: we can’t make an `&x` while `y` is in scope. | |
231 | ||
232 | So when we add the curly braces: | |
233 | ||
234 | ```rust | |
235 | let mut x = 5; | |
236 | ||
b039eaaf | 237 | { |
bd371182 AL |
238 | let y = &mut x; // -+ &mut borrow starts here |
239 | *y += 1; // | | |
240 | } // -+ ... and ends here | |
241 | ||
242 | println!("{}", x); // <- try to borrow x here | |
243 | ``` | |
244 | ||
245 | There’s no problem. Our mutable borrow goes out of scope before we create an | |
246 | immutable one. But scope is the key to seeing how long a borrow lasts for. | |
247 | ||
248 | ## Issues borrowing prevents | |
249 | ||
250 | Why have these restrictive rules? Well, as we noted, these rules prevent data | |
251 | races. What kinds of issues do data races cause? Here’s a few. | |
252 | ||
253 | ### Iterator invalidation | |
254 | ||
255 | One example is ‘iterator invalidation’, which happens when you try to mutate a | |
256 | collection that you’re iterating over. Rust’s borrow checker prevents this from | |
257 | happening: | |
258 | ||
259 | ```rust | |
260 | let mut v = vec![1, 2, 3]; | |
261 | ||
262 | for i in &v { | |
263 | println!("{}", i); | |
264 | } | |
265 | ``` | |
266 | ||
9cc50fc6 | 267 | This prints out one through three. As we iterate through the vector, we’re |
bd371182 AL |
268 | only given references to the elements. And `v` is itself borrowed as immutable, |
269 | which means we can’t change it while we’re iterating: | |
270 | ||
271 | ```rust,ignore | |
272 | let mut v = vec![1, 2, 3]; | |
273 | ||
274 | for i in &v { | |
275 | println!("{}", i); | |
276 | v.push(34); | |
277 | } | |
278 | ``` | |
279 | ||
280 | Here’s the error: | |
281 | ||
282 | ```text | |
283 | error: cannot borrow `v` as mutable because it is also borrowed as immutable | |
284 | v.push(34); | |
285 | ^ | |
286 | note: previous borrow of `v` occurs here; the immutable borrow prevents | |
287 | subsequent moves or mutable borrows of `v` until the borrow ends | |
288 | for i in &v { | |
289 | ^ | |
290 | note: previous borrow ends here | |
291 | for i in &v { | |
292 | println!(“{}”, i); | |
293 | v.push(34); | |
294 | } | |
295 | ^ | |
296 | ``` | |
297 | ||
298 | We can’t modify `v` because it’s borrowed by the loop. | |
299 | ||
300 | ### use after free | |
301 | ||
e9174d1e SL |
302 | References must not live longer than the resource they refer to. Rust will |
303 | check the scopes of your references to ensure that this is true. | |
bd371182 | 304 | |
62682a34 | 305 | If Rust didn’t check this property, we could accidentally use a reference |
bd371182 AL |
306 | which was invalid. For example: |
307 | ||
308 | ```rust,ignore | |
309 | let y: &i32; | |
b039eaaf | 310 | { |
bd371182 AL |
311 | let x = 5; |
312 | y = &x; | |
313 | } | |
314 | ||
315 | println!("{}", y); | |
316 | ``` | |
317 | ||
318 | We get this error: | |
319 | ||
320 | ```text | |
321 | error: `x` does not live long enough | |
322 | y = &x; | |
323 | ^ | |
324 | note: reference must be valid for the block suffix following statement 0 at | |
325 | 2:16... | |
326 | let y: &i32; | |
b039eaaf | 327 | { |
bd371182 AL |
328 | let x = 5; |
329 | y = &x; | |
330 | } | |
331 | ||
332 | note: ...but borrowed value is only valid for the block suffix following | |
333 | statement 0 at 4:18 | |
334 | let x = 5; | |
335 | y = &x; | |
336 | } | |
337 | ``` | |
338 | ||
339 | In other words, `y` is only valid for the scope where `x` exists. As soon as | |
340 | `x` goes away, it becomes invalid to refer to it. As such, the error says that | |
341 | the borrow ‘doesn’t live long enough’ because it’s not valid for the right | |
342 | amount of time. | |
343 | ||
c1a9b12d SL |
344 | The same problem occurs when the reference is declared _before_ the variable it |
345 | refers to. This is because resources within the same scope are freed in the | |
346 | opposite order they were declared: | |
bd371182 AL |
347 | |
348 | ```rust,ignore | |
349 | let y: &i32; | |
350 | let x = 5; | |
351 | y = &x; | |
352 | ||
353 | println!("{}", y); | |
354 | ``` | |
355 | ||
356 | We get this error: | |
357 | ||
358 | ```text | |
359 | error: `x` does not live long enough | |
360 | y = &x; | |
361 | ^ | |
362 | note: reference must be valid for the block suffix following statement 0 at | |
363 | 2:16... | |
364 | let y: &i32; | |
365 | let x = 5; | |
366 | y = &x; | |
b039eaaf | 367 | |
bd371182 AL |
368 | println!("{}", y); |
369 | } | |
370 | ||
371 | note: ...but borrowed value is only valid for the block suffix following | |
372 | statement 1 at 3:14 | |
373 | let x = 5; | |
374 | y = &x; | |
b039eaaf | 375 | |
bd371182 AL |
376 | println!("{}", y); |
377 | } | |
378 | ``` | |
c1a9b12d SL |
379 | |
380 | In the above example, `y` is declared before `x`, meaning that `y` lives longer | |
381 | than `x`, which is not allowed. |