@@ -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