在Rust中,向量的所有权转移是否会改变其内存位置?

3

考虑以下代码片段:

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);
}

以及输出

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

为什么v1v2的值不同?

  1. 首先,&v2真的表示向量vec![1,2,3]在第二行声明的地址吗?
  2. 如果将v1的值复制v2,那么向量不应该具有复制特性(trait)吗?
  3. 如果v1的值被移动到由v2标识的新内存位置,那么为什么还需要这样做呢?为什么v2不直接指向v1的内存位置,因为所有权已经转移到了v2,所以v1基本上是没用的,除非我重新分配它(简而言之,如果它是内存复制,为什么所有权转移需要一个memcpy?)
  4. v1再次赋值给v2时,我如何得到相同的地址位置?

这个回答解决了你的问题吗?Rust中的移动语义是什么? - Denys Séguret
从链接的问答中提取的一个答案摘录:“从概念上讲,移动某物不需要做任何事情” - Denys Séguret
@DenysSéguret,其中的答案部分地回答了问题,但它们并没有具体回答有关地址的问题,例如问题1在那里没有得到回答。 - JavaTechnical
3个回答

5
你混淆了数据的地址和变量的地址。一开始,你的内存看起来像这样:
    Stack               Heap
+----+------+---+       +---+---+---+
|    | len  | 3 |   +-->| 1 | 2 | 3 |
| v1 +------+---+   |   +---+---+---+
|    | data     |---+
+----+------+---+
|    | len  | _ |
| v2 +------+---+
|    | data     |
+----+----------+

在你执行 let v2 = v1 后,它看起来像这样:

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

请注意,v1v2的位置并未改变,堆上的数据位置也没有改变,但是v1字段的值已经移动到v2中。此时,v1字段的值无效。

然后当您执行v1 = v2时,会回到第一个配置。

另外,您的println语句打印了堆栈上变量v1v2的地址。

请注意,如果打印&v1[0](或&v2[0]),您将获得堆上数据的地址,并且可以看到它不会改变(playground)


那么,这意味着当我说 v2=v1 时,堆的地址实际上被复制(memcpy)到 v2.data 中,对吗? - JavaTechnical
此外,你说要使用&v1[0]来获取向量中数据的地址,那么对于用户自定义的struct,我们应该如何做呢? - JavaTechnical
为了完整性,向量也有容量(这在这里并不是很相关)。 - Sven Marnach
1
@JavaTechnical 移动向量可能会复制其元数据(即长度、容量、数据指针)。如果编译器可以证明不需要复制,则可以自由地消除该副本。实际数据永远不会被复制。 - Sven Marnach
@Jmb 为了获取 vec('s buffer)的地址,您还可以使用&*(或.as_slice()&[..])将其“转换”为切片。而不是尝试访问它可能具有或可能没有的第一个元素。 - Masklinn
显示剩余3条评论

2
为什么v1v2的值不同?
它们是栈中的不同变量,为什么会相同呢?
相同的是堆中的存储。
首先,&v2 真的意味着地址在第二行声明的vec![1,2,3]向量吗?
不是的,它表示借用了v2。你看到的是{:p}被实现为引用类型,它打印出它们的地址。
如果将v1的值复制v2,那么这个向量就应该有一个copy trait,对吗?
大多数情况下,您不希望在堆中分配内存并进行复制。
如果把v1的值移到由v2标识的新存储位置,为什么需要这样做?为什么v2不能直接指向v1的内存位置,因为所有权已经转移给v2,而且在我重新赋值之前v1完全无用(简言之,如果这是一个内存副本,为什么所有权转移需要memcpy)?
事实上,这就是发生的情况。它是一次移动,所以只有向量的位被复制,而不是堆中的内容。
v1再次赋值给v2时,我怎么会得到相同的地址位置?
为什么会不同?它仍然是栈中的同一位置。

-3
  1. &v2 是对 v2 的引用,不是 v2 的地址。
  2. v2 不是 v1 的副本,它是一次“移动”,但由于它是相同的作用域,所以实际上什么也没有发生。
  3. v1 现在是 v2 的移动值,同样是相同的作用域,因此可以重复使用相同的地址。
  4. 地址不是变量的同义词。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接