Skip to content

Commit 50d179e

Browse files
committed
Explained the data race with an example.
1 parent 62b3b40 commit 50d179e

File tree

1 file changed

+30
-10
lines changed

1 file changed

+30
-10
lines changed

src/doc/book/ownership.md

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@ special annotation here, it’s the default thing that Rust does.
122122
## The details
123123

124124
The reason that we cannot use a binding after we’ve moved it is subtle, but
125-
important. When we write code like this:
125+
important.
126+
127+
When we write code like this:
126128

127129
```rust
128130
let x = 10;
@@ -140,23 +142,41 @@ let v = vec![1, 2, 3];
140142
let v2 = v;
141143
```
142144

143-
The first line allocates memory for the vector object, `v`, on the stack like
145+
The first line allocates memory for the vector object `v` on the stack like
144146
it does for `x` above. But in addition to that it also allocates some memory
145-
on on the [heap][sh] for the actual data `[1, 2, 3]`. Rust copies the address
146-
of this heap allocation to an internal pointer part of the vector object
147-
placed on the stack (let's call it the data pointer). It is worth pointing out
148-
even at the risk of being redundant that the vector object and its data live
149-
in separate memory regions instead of being a single contiguous memory
150-
allocation (due to reasons we will not go into at this point of time).
147+
on the [heap][sh] for the actual data (`[1, 2, 3]`). Rust copies the address
148+
of this heap allocation to an internal pointer, which is part of the vector
149+
object placed on the stack (let's call it the data pointer).
150+
151+
It is worth pointing out (even at the risk of repeating things) that the vector
152+
object and its data live in separate memory regions instead of being a single
153+
contiguous memory allocation (due to reasons we will not go into at this point
154+
of time). These two parts of the vector (the one on the stack and one on the
155+
heap) must agree with each other at all times with regards to things like the
156+
length, capacity etc.
151157

152158
When we move `v` to `v2`, rust actually does a bitwise copy of the vector
153159
object `v` into the stack allocation represented by `v2`. This shallow copy
154160
does not create a copy of the heap allocation containing the actual data.
155161
Which means that there would be two pointers to the contents of the vector
156162
both pointing to the same memory allocation on the heap. It would violate
157163
Rust’s safety guarantees by introducing a data race if one could access both
158-
`v` and `v2` at the same time. Therefore, Rust forbids using `v` after we’ve
159-
done the move (shallow copy).
164+
`v` and `v2` at the same time.
165+
166+
For example if we truncated the vector to just two elements through `v2`:
167+
168+
```rust
169+
v2.truncate(2);
170+
```
171+
172+
and `v1` were still accessible we'd end up with an invalid vector since it
173+
would not know that the heap data has been truncated. Now, the part of the
174+
vector `v1` on the stack does not agree with its corresponding part on the
175+
heap. `v1` still thinks there are three elements in the vector and will
176+
happily let us access the non existent element `v1[2]` but as you might
177+
already know this is a recipe for disaster.
178+
179+
This is why Rust forbids using `v` after we’ve done the move.
160180

161181
[sh]: the-stack-and-the-heap.html
162182

0 commit comments

Comments
 (0)