@@ -124,21 +124,65 @@ special annotation here, it’s the default thing that Rust does.
124
124
## The details
125
125
126
126
The reason that we cannot use a binding after we’ve moved it is subtle, but
127
- important. When we write code like this:
127
+ important.
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:
128
140
129
141
```rust
130
142
let v = vec![1, 2, 3];
131
143
132
- let v2 = v;
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
151
+ object placed on the stack (let's call it the data pointer).
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
166
+ `v` and `v2` at the same time.
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);
133
174
```
134
175
135
- The first line allocates memory for the vector object, `v`, and for the data it
136
- contains. The vector object is stored on the [stack][sh] and contains a pointer
137
- to the content (`[1, 2, 3]`) stored on the [heap][sh]. When we move `v` to `v2`,
138
- it creates a copy of that pointer, for `v2`. Which means that there would be two
139
- pointers to the content of the vector on the heap. It would violate Rust’s
140
- safety guarantees by introducing a data race. Therefore, Rust forbids using `v`
141
- after we’ve done the move.
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.
142
186
143
187
[sh]: the-stack-and-the-heap.html
144
188
0 commit comments