diff --git a/src/doc/book/ownership.md b/src/doc/book/ownership.md index a62d31d362b14..ac6184013ee23 100644 --- a/src/doc/book/ownership.md +++ b/src/doc/book/ownership.md @@ -122,21 +122,65 @@ 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; +``` + +Rust allocates memory for an integer [i32] on the [stack][sh], copies the bit +pattern representing the value of 10 to the allocated memory and binds the +variable name x to this memory region for future reference. + +Now consider the following code fragment: ```rust let v = vec![1, 2, 3]; -let v2 = v; +let mut v2 = v; +``` + +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 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 stating the obvious) 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 +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. + +For example if we truncated the vector to just two elements through `v2`: + +```rust +# let v = vec![1, 2, 3]; +# let mut v2 = v; +v2.truncate(2); ``` -The first line allocates memory for the vector object, `v`, and for the data it -contains. The vector object is stored on the [stack][sh] and contains a pointer -to the content (`[1, 2, 3]`) stored on the [heap][sh]. When we move `v` to `v2`, -it creates a copy of that pointer, for `v2`. Which means that there would be two -pointers to the content of the vector on the heap. It would violate Rust’s -safety guarantees by introducing a data race. Therefore, Rust forbids using `v` -after we’ve done the move. +and `v1` were still accessible we'd end up with an invalid vector since `v1` +would not know that the heap data has been truncated. Now, the part of the +vector `v1` on the stack does not agree with the 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. Especially because it might lead +to a segmentation fault or worse allow an unauthorized user to read from +memory to which they don't have access. + +This is why Rust forbids using `v` after we’ve done the move. [sh]: the-stack-and-the-heap.html