Explained the data race with an example.
This commit is contained in:
parent
62b3b40ade
commit
50d179e062
@ -122,7 +122,9 @@ special annotation here, it’s the default thing that Rust does.
|
||||
## The details
|
||||
|
||||
The reason that we cannot use a binding after we’ve moved it is subtle, but
|
||||
important. When we write code like this:
|
||||
important.
|
||||
|
||||
When we write code like this:
|
||||
|
||||
```rust
|
||||
let x = 10;
|
||||
@ -140,14 +142,18 @@ let v = vec![1, 2, 3];
|
||||
let v2 = v;
|
||||
```
|
||||
|
||||
The first line allocates memory for the vector object, `v`, on the stack like
|
||||
The first line allocates memory for the vector object `v` on the stack like
|
||||
it does for `x` above. But in addition to that it also allocates some memory
|
||||
on on the [heap][sh] for the actual data `[1, 2, 3]`. Rust copies the address
|
||||
of this heap allocation to an internal pointer part of the vector object
|
||||
placed on the stack (let's call it the data pointer). It is worth pointing out
|
||||
even at the risk of being redundant that the vector object and its data live
|
||||
in separate memory regions instead of being a single contiguous memory
|
||||
allocation (due to reasons we will not go into at this point of time).
|
||||
on the [heap][sh] for the actual data (`[1, 2, 3]`). Rust copies the address
|
||||
of this heap allocation to an internal pointer, which is part of the vector
|
||||
object placed on the stack (let's call it the data pointer).
|
||||
|
||||
It is worth pointing out (even at the risk of repeating things) that the vector
|
||||
object and its data live in separate memory regions instead of being a single
|
||||
contiguous memory allocation (due to reasons we will not go into at this point
|
||||
of time). These two parts of the vector (the one on the stack and one on the
|
||||
heap) must agree with each other at all times with regards to things like the
|
||||
length, capacity etc.
|
||||
|
||||
When we move `v` to `v2`, rust actually does a bitwise copy of the vector
|
||||
object `v` into the stack allocation represented by `v2`. This shallow copy
|
||||
@ -155,8 +161,22 @@ does not create a copy of the heap allocation containing the actual data.
|
||||
Which means that there would be two pointers to the contents of the vector
|
||||
both pointing to the same memory allocation on the heap. It would violate
|
||||
Rust’s safety guarantees by introducing a data race if one could access both
|
||||
`v` and `v2` at the same time. Therefore, Rust forbids using `v` after we’ve
|
||||
done the move (shallow copy).
|
||||
`v` and `v2` at the same time.
|
||||
|
||||
For example if we truncated the vector to just two elements through `v2`:
|
||||
|
||||
```rust
|
||||
v2.truncate(2);
|
||||
```
|
||||
|
||||
and `v1` were still accessible we'd end up with an invalid vector since it
|
||||
would not know that the heap data has been truncated. Now, the part of the
|
||||
vector `v1` on the stack does not agree with its corresponding part on the
|
||||
heap. `v1` still thinks there are three elements in the vector and will
|
||||
happily let us access the non existent element `v1[2]` but as you might
|
||||
already know this is a recipe for disaster.
|
||||
|
||||
This is why Rust forbids using `v` after we’ve done the move.
|
||||
|
||||
[sh]: the-stack-and-the-heap.html
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user