Skip to content

Commit 9f0e39b

Browse files
committed
Rollup merge of rust-lang#31565 - SDX2000:docfixes4, r=steveklabnik
See title and diff for more information.
2 parents 2051a92 + 1536195 commit 9f0e39b

File tree

1 file changed

+53
-9
lines changed

1 file changed

+53
-9
lines changed

src/doc/book/ownership.md

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -124,21 +124,65 @@ special annotation here, it’s the default thing that Rust does.
124124
## The details
125125

126126
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:
128140

129141
```rust
130142
let v = vec![1, 2, 3];
131143

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);
133174
```
134175

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.
142186

143187
[sh]: the-stack-and-the-heap.html
144188

0 commit comments

Comments
 (0)