]>
Commit | Line | Data |
---|---|---|
85aaf69f | 1 | % Ownership |
1a4d82fc | 2 | |
bd371182 AL |
3 | This guide is one of three presenting Rust’s ownership system. This is one of |
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, which you’re reading now | |
10 | * [borrowing][borrowing], and their associated feature ‘references’ | |
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 | [borrowing]: references-and-borrowing.html | |
17 | [lifetimes]: lifetimes.html | |
1a4d82fc JJ |
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 | |
bd371182 | 24 | ‘zero-cost abstractions’, which means that in Rust, abstractions cost as little |
1a4d82fc | 25 | as possible in order to make them work. The ownership system is a prime example |
bd371182 | 26 | of a zero-cost abstraction. All of the analysis we’ll talk about in this guide |
1a4d82fc JJ |
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 | |
bd371182 AL |
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. | |
1a4d82fc JJ |
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 | ||
bd371182 | 40 | With that in mind, let’s learn about ownership. |
1a4d82fc JJ |
41 | |
42 | # Ownership | |
43 | ||
bd371182 | 44 | [Variable bindings][bindings] have a property in Rust: they ‘have ownership’ |
b039eaaf | 45 | of what they’re bound to. This means that when a binding goes out of scope, |
c1a9b12d | 46 | Rust will free the bound resources. For example: |
1a4d82fc JJ |
47 | |
48 | ```rust | |
bd371182 AL |
49 | fn foo() { |
50 | let v = vec![1, 2, 3]; | |
1a4d82fc JJ |
51 | } |
52 | ``` | |
53 | ||
54a0048b | 54 | When `v` comes into scope, a new [vector][vectors] is created on [the stack][stack], |
7453a54e SL |
55 | and it allocates space on [the heap][heap] for its elements. When `v` goes out |
56 | of scope at the end of `foo()`, Rust will clean up everything related to the | |
57 | vector, even the heap-allocated memory. This happens deterministically, at the | |
58 | end of the scope. | |
1a4d82fc | 59 | |
9cc50fc6 SL |
60 | We'll cover [vectors] in detail later in this chapter; we only use them |
61 | here as an example of a type that allocates space on the heap at runtime. They | |
62 | behave like [arrays], except their size may change by `push()`ing more | |
63 | elements onto them. | |
64 | ||
65 | Vectors have a [generic type][generics] `Vec<T>`, so in this example `v` will have type | |
66 | `Vec<i32>`. We'll cover generics in detail later in this chapter. | |
67 | ||
68 | [arrays]: primitive-types.html#arrays | |
69 | [vectors]: vectors.html | |
bd371182 | 70 | [heap]: the-stack-and-the-heap.html |
7453a54e | 71 | [stack]: the-stack-and-the-heap.html#the-stack |
bd371182 | 72 | [bindings]: variable-bindings.html |
9cc50fc6 | 73 | [generics]: generics.html |
1a4d82fc | 74 | |
bd371182 | 75 | # Move semantics |
1a4d82fc | 76 | |
bd371182 AL |
77 | There’s some more subtlety here, though: Rust ensures that there is _exactly |
78 | one_ binding to any given resource. For example, if we have a vector, we can | |
79 | assign it to another binding: | |
1a4d82fc JJ |
80 | |
81 | ```rust | |
bd371182 | 82 | let v = vec![1, 2, 3]; |
1a4d82fc | 83 | |
bd371182 | 84 | let v2 = v; |
1a4d82fc JJ |
85 | ``` |
86 | ||
bd371182 | 87 | But, if we try to use `v` afterwards, we get an error: |
1a4d82fc | 88 | |
bd371182 AL |
89 | ```rust,ignore |
90 | let v = vec![1, 2, 3]; | |
1a4d82fc | 91 | |
bd371182 | 92 | let v2 = v; |
1a4d82fc | 93 | |
bd371182 | 94 | println!("v[0] is: {}", v[0]); |
1a4d82fc JJ |
95 | ``` |
96 | ||
bd371182 | 97 | It looks like this: |
85aaf69f | 98 | |
bd371182 AL |
99 | ```text |
100 | error: use of moved value: `v` | |
101 | println!("v[0] is: {}", v[0]); | |
102 | ^ | |
1a4d82fc JJ |
103 | ``` |
104 | ||
bd371182 AL |
105 | A similar thing happens if we define a function which takes ownership, and |
106 | try to use something after we’ve passed it as an argument: | |
1a4d82fc | 107 | |
bd371182 AL |
108 | ```rust,ignore |
109 | fn take(v: Vec<i32>) { | |
110 | // what happens here isn’t important. | |
1a4d82fc | 111 | } |
1a4d82fc | 112 | |
bd371182 | 113 | let v = vec![1, 2, 3]; |
1a4d82fc | 114 | |
bd371182 | 115 | take(v); |
1a4d82fc | 116 | |
bd371182 | 117 | println!("v[0] is: {}", v[0]); |
1a4d82fc JJ |
118 | ``` |
119 | ||
bd371182 AL |
120 | Same error: ‘use of moved value’. When we transfer ownership to something else, |
121 | we say that we’ve ‘moved’ the thing we refer to. You don’t need some sort of | |
122 | special annotation here, it’s the default thing that Rust does. | |
1a4d82fc | 123 | |
bd371182 | 124 | ## The details |
1a4d82fc | 125 | |
bd371182 | 126 | The reason that we cannot use a binding after we’ve moved it is subtle, but |
54a0048b | 127 | important. |
7453a54e SL |
128 | |
129 | When we write code like this: | |
130 | ||
131 | ```rust | |
132 | let x = 10; | |
133 | ``` | |
134 | ||
135 | Rust allocates memory for an integer [i32] on the [stack][sh], copies the bit | |
136 | pattern representing the value of 10 to the allocated memory and binds the | |
137 | variable name x to this memory region for future reference. | |
138 | ||
139 | Now consider the following code fragment: | |
1a4d82fc JJ |
140 | |
141 | ```rust | |
bd371182 | 142 | let v = vec![1, 2, 3]; |
1a4d82fc | 143 | |
7453a54e SL |
144 | let mut v2 = v; |
145 | ``` | |
146 | ||
147 | The first line allocates memory for the vector object `v` on the stack like | |
148 | it does for `x` above. But in addition to that it also allocates some memory | |
149 | on the [heap][sh] for the actual data (`[1, 2, 3]`). Rust copies the address | |
150 | of this heap allocation to an internal pointer, which is part of the vector | |
54a0048b | 151 | object placed on the stack (let's call it the data pointer). |
7453a54e SL |
152 | |
153 | It is worth pointing out (even at the risk of stating the obvious) that the | |
154 | vector object and its data live in separate memory regions instead of being a | |
155 | single contiguous memory allocation (due to reasons we will not go into at | |
156 | this point of time). These two parts of the vector (the one on the stack and | |
157 | one on the heap) must agree with each other at all times with regards to | |
158 | things like the length, capacity etc. | |
159 | ||
160 | When we move `v` to `v2`, Rust actually does a bitwise copy of the vector | |
161 | object `v` into the stack allocation represented by `v2`. This shallow copy | |
162 | does not create a copy of the heap allocation containing the actual data. | |
163 | Which means that there would be two pointers to the contents of the vector | |
164 | both pointing to the same memory allocation on the heap. It would violate | |
165 | Rust’s safety guarantees by introducing a data race if one could access both | |
54a0048b | 166 | `v` and `v2` at the same time. |
7453a54e SL |
167 | |
168 | For example if we truncated the vector to just two elements through `v2`: | |
169 | ||
170 | ```rust | |
171 | # let v = vec![1, 2, 3]; | |
172 | # let mut v2 = v; | |
173 | v2.truncate(2); | |
1a4d82fc JJ |
174 | ``` |
175 | ||
7453a54e SL |
176 | and `v1` were still accessible we'd end up with an invalid vector since `v1` |
177 | would not know that the heap data has been truncated. Now, the part of the | |
178 | vector `v1` on the stack does not agree with the corresponding part on the | |
179 | heap. `v1` still thinks there are three elements in the vector and will | |
180 | happily let us access the non existent element `v1[2]` but as you might | |
181 | already know this is a recipe for disaster. Especially because it might lead | |
182 | to a segmentation fault or worse allow an unauthorized user to read from | |
183 | memory to which they don't have access. | |
184 | ||
185 | This is why Rust forbids using `v` after we’ve done the move. | |
1a4d82fc | 186 | |
bd371182 | 187 | [sh]: the-stack-and-the-heap.html |
1a4d82fc | 188 | |
bd371182 AL |
189 | It’s also important to note that optimizations may remove the actual copy of |
190 | the bytes on the stack, depending on circumstances. So it may not be as | |
191 | inefficient as it initially seems. | |
1a4d82fc | 192 | |
bd371182 | 193 | ## `Copy` types |
1a4d82fc | 194 | |
bd371182 AL |
195 | We’ve established that when ownership is transferred to another binding, you |
196 | cannot use the original binding. However, there’s a [trait][traits] that changes this | |
197 | behavior, and it’s called `Copy`. We haven’t discussed traits yet, but for now, | |
198 | you can think of them as an annotation to a particular type that adds extra | |
199 | behavior. For example: | |
1a4d82fc JJ |
200 | |
201 | ```rust | |
bd371182 | 202 | let v = 1; |
1a4d82fc | 203 | |
bd371182 | 204 | let v2 = v; |
1a4d82fc | 205 | |
bd371182 | 206 | println!("v is: {}", v); |
1a4d82fc JJ |
207 | ``` |
208 | ||
bd371182 AL |
209 | In this case, `v` is an `i32`, which implements the `Copy` trait. This means |
210 | that, just like a move, when we assign `v` to `v2`, a copy of the data is made. | |
211 | But, unlike a move, we can still use `v` afterward. This is because an `i32` | |
212 | has no pointers to data somewhere else, copying it is a full copy. | |
1a4d82fc | 213 | |
62682a34 SL |
214 | All primitive types implement the `Copy` trait and their ownership is |
215 | therefore not moved like one would assume, following the ´ownership rules´. | |
b039eaaf SL |
216 | To give an example, the two following snippets of code only compile because the |
217 | `i32` and `bool` types implement the `Copy` trait. | |
62682a34 SL |
218 | |
219 | ```rust | |
220 | fn main() { | |
221 | let a = 5; | |
222 | ||
223 | let _y = double(a); | |
224 | println!("{}", a); | |
225 | } | |
226 | ||
227 | fn double(x: i32) -> i32 { | |
228 | x * 2 | |
229 | } | |
230 | ``` | |
231 | ||
232 | ```rust | |
233 | fn main() { | |
234 | let a = true; | |
235 | ||
236 | let _y = change_truth(a); | |
237 | println!("{}", a); | |
238 | } | |
239 | ||
240 | fn change_truth(x: bool) -> bool { | |
241 | !x | |
242 | } | |
243 | ``` | |
244 | ||
92a42be0 | 245 | If we had used types that do not implement the `Copy` trait, |
62682a34 SL |
246 | we would have gotten a compile error because we tried to use a moved value. |
247 | ||
248 | ```text | |
249 | error: use of moved value: `a` | |
250 | println!("{}", a); | |
251 | ^ | |
252 | ``` | |
253 | ||
bd371182 AL |
254 | We will discuss how to make your own types `Copy` in the [traits][traits] |
255 | section. | |
1a4d82fc | 256 | |
bd371182 | 257 | [traits]: traits.html |
1a4d82fc | 258 | |
bd371182 | 259 | # More than ownership |
1a4d82fc | 260 | |
bd371182 | 261 | Of course, if we had to hand ownership back with every function we wrote: |
1a4d82fc JJ |
262 | |
263 | ```rust | |
bd371182 AL |
264 | fn foo(v: Vec<i32>) -> Vec<i32> { |
265 | // do stuff with v | |
1a4d82fc | 266 | |
bd371182 AL |
267 | // hand back ownership |
268 | v | |
1a4d82fc JJ |
269 | } |
270 | ``` | |
271 | ||
bd371182 | 272 | This would get very tedious. It gets worse the more things we want to take ownership of: |
1a4d82fc JJ |
273 | |
274 | ```rust | |
bd371182 AL |
275 | fn foo(v1: Vec<i32>, v2: Vec<i32>) -> (Vec<i32>, Vec<i32>, i32) { |
276 | // do stuff with v1 and v2 | |
1a4d82fc | 277 | |
bd371182 AL |
278 | // hand back ownership, and the result of our function |
279 | (v1, v2, 42) | |
1a4d82fc JJ |
280 | } |
281 | ||
bd371182 AL |
282 | let v1 = vec![1, 2, 3]; |
283 | let v2 = vec![1, 2, 3]; | |
1a4d82fc | 284 | |
bd371182 | 285 | let (v1, v2, answer) = foo(v1, v2); |
1a4d82fc JJ |
286 | ``` |
287 | ||
bd371182 AL |
288 | Ugh! The return type, return line, and calling the function gets way more |
289 | complicated. | |
1a4d82fc | 290 | |
bd371182 AL |
291 | Luckily, Rust offers a feature, borrowing, which helps us solve this problem. |
292 | It’s the topic of the next section! | |
1a4d82fc | 293 |