3

Consider the following code snippet:

fn main() {   
    let mut v1 = vec![1, 2, 3];
    println!("The address of vector v1 is {:p}", &v1);
    let v2 = v1;
    println!("The address of vector v2 is {:p}", &v2);
    v1 = v2;
    println!("The address of vector v1 is {:p}", &v1);
}

and the output

The address of vector v1 is 0x7fff253de570
The address of vector v2 is 0x7fff253de5e0
The address of vector v1 is 0x7fff253de570

Why does the value of v1 and v2 not the same?

  1. First, Doesn't &v2 really mean the address of vector vec![1,2,3] as declared in line #2?
  2. If the value of v1 is copied to v2 then, should not the vector have a copy trait?
  3. If the value of v1 is moved to a new memory location which is identified by v2, why is it at all required, why doesn't the v2 simply point to the memory location of v1, because the ownership is already transferred to v2, and v1 is pretty much useless until I assign it back (in short, if it is a memory copy, why does the ownership transfer require a memcpy?)
  4. When v1 is assigned to v2 again, how did I get the same address location?
JavaTechnical
  • 8,846
  • 8
  • 61
  • 97
  • Does this answer your question? [What are move semantics in Rust?](https://stackoverflow.com/questions/30288782/what-are-move-semantics-in-rust) – Denys Séguret Jun 02 '21 at 09:57
  • Extract of one of the answers of the linked QA: **"Conceptually, moving something doesn't need to do anything"** – Denys Séguret Jun 02 '21 at 09:58
  • @DenysSéguret The answers in it do *partially* answer the question, they do not answer specifically about addresses, for example question 1 isn't answered there. – JavaTechnical Jun 02 '21 at 17:03

3 Answers3

5

You're confusing the address of the data and the address of the variable. In the beginning, your memory looks something like this:

    Stack               Heap
+----+------+---+       +---+---+---+
|    | len  | 3 |   +-->| 1 | 2 | 3 |
| v1 +------+---+   |   +---+---+---+
|    | data     |---+
+----+------+---+
|    | len  | _ |
| v2 +------+---+
|    | data     |
+----+----------+

After you do let v2 = v1, it looks like this:

    Stack               Heap
+----+------+---+       +---+---+---+
|    | len  | _ |   +-->| 1 | 2 | 3 |
| v1 +------+---+   |   +---+---+---+
|    | data     |   |
+----+------+---+   |
|    | len  | 3 |   |
| v2 +------+---+   |
|    | data     |---+
+----+----------+

Note that the locations of v1 and v2 have not changed, and neither has the location of the data on the heap, but the values of the fields of v1 have been moved into v2. At that point, the values of the fields of v1 are invalid.

Then when you do v1 = v2, you go back to the first configuration.

OTOH your println statements print the address of the variables v1 and v2 on the stack.

Note that if you print &v1[0] (resp. &v2[0]), you will get the address of the data on the heap and see that it doesn't change (playground)

Jmb
  • 18,893
  • 2
  • 28
  • 55
  • So, that means the address of the heap is actually *copied* (`memcpy`) to `v2.data` when I say `v2=v1`, right? – JavaTechnical Jun 02 '21 at 11:47
  • Also, you said to use `&v1[0]` to get the address of the data in the vector, how do we do it for user-defined `struct`? – JavaTechnical Jun 02 '21 at 11:57
  • For completeness, a vector also has a capacity (which is not really relevant here). – Sven Marnach Jun 02 '21 at 12:55
  • 1
    @JavaTechnical Moving a vector _might_ copy its metadata (i.e. length, capacity, data pointer). The compiler is free to eliminate the copy if it can proof it's not needed. The actual data is never copied. – Sven Marnach Jun 02 '21 at 12:57
  • If your user-definied struct allocates memory on the heap, it depends on the public interface of your struct how to get the address of that memory. It might not be exposed at all. – Sven Marnach Jun 02 '21 at 12:58
  • @SvenMarnach ok, since the vector has implemented an index trait and has put the data at the 0th index, we get the address of the data on the heap. For custom structs, either we would need to implement a method or follow the vector approach, right? – JavaTechnical Jun 02 '21 at 13:09
  • @Jmb in order to get the address of the vec('s buffer) you can also "cast" it to a slice using `&*` (or `.as_slice()`, or `&[..]`). rather than try to access a first element it may or may not have. – Masklinn Jun 02 '21 at 13:37
  • From a low-level (HW) standpoint, there is no difference between a copy and a move. They both translate to the same assembly involving a `memcpy` (which may be optimized away if the compiler can prove that it's not needed). The difference is that in the case of a "move", the compiler considers that the source data is no longer valid, whereas for a "copy" both source and destination are valid afterwards. – Jmb Jun 02 '21 at 13:53
2

Why does the value of v1 and v2 not the same?

Why would it be the same? They are different variables in the stack.

What is the same is the storage in the heap.

  1. First, Doesn't &v2 really mean the address of vector vec![1,2,3] as declared in line #2?

No, it means borrowing v2. What you see is {:p} being implemented for references which prints their address.

  1. If the value of v1 is copied to v2 then, should not the vector have a copy trait?

No, in most cases you do not want to allocate memory in the heap and copy it.

  1. If the value of v1 is moved to a new memory location which is identified by v2, why is it at all required, why doesn't the v2 simply point to the memory location of v1, because the ownership is already transferred to v2, and v1 is pretty much useless until I assign it back (in short, if it is a memory copy, why does the ownership transfer require a memcpy?)

That is what actually occurs. It is a move, so only the bits of the vectors are copied, not the contents in the heap.

  1. When v1 is assigned to v2 again, how did I get the same address location?

Why would it be different? It is still the same palce in the stack.

Acorn
  • 24,970
  • 5
  • 40
  • 69
-3
  1. &v2 is a reference to v2, not the address of v2.
  2. v2 is not a copy of v1, it is a "move", but since it is the same scope, nothing really happens.
  3. v1 is now the moved value of v2, again same scope, so the same address can be reused.
  4. Address is not the same thing as a variable.
Aron
  • 15,464
  • 3
  • 31
  • 64