我试图在包含具有实现
Copy
特征的类型的字段的结构的情况下理解 Rust 的移动语义。考虑以下结构:
struct Book {
author: &'static str,
title: &'static str,
year: u32,
}
fn main() {
let bookA = Book {
author: "Douglas Hofstadter",
title: "Gödel, Escher, Bach",
year: 1979,
};
let addr_bookA: *const Book = &bookA;
let bookB = bookA; // Move occurs here
// Getting the memory addresses as raw pointers
let addr_bookB: *const Book = &bookB;
println!("Address of bookA: {:p}", addr_bookA);
println!("Address of bookB: {:p}", addr_bookB);
}
输出:
Address of bookA: 0x7fff20b1a0b0
Address of bookB: 0x7fff20b1a0e0
假设有一个移动并且 bookB 占用了分配给 bookA 的值,我期望当
bookA
分配给 bookB
时得到数据的浅表副本。然而,当我检查这两个变量的内存地址时,它们是不同的。
有人可以帮忙澄清以下几点吗?
为什么结构体的移动仍然会导致原始变量和移动到的变量产生不同的内存地址?
在这种情况下数据是否真的被复制了,如果是的话,为什么 Rust 将其视为一次移动?
在这种情况下数据是否真的被复制了,如果是的话,为什么 Rust 将其视为一次移动?
因为移动和副本之间的唯一区别在于,副本仍然可以使用原始副本。 语义上,两者都是 memcpy(因此将源字节复制到目标)。
这些副本可能会被优化掉,但这与语义无关。
为什么结构体的移动仍然会导致原始变量和移动到的变量产生不同的内存地址?因为 LLVM 不会优化掉副本。我承认,我不知道为什么,我曾经假设 LLVM 不会看到同一事物的 SSA 版本之间的差异,但显然它确实看到了,并且拒绝或无法通过删除副本来优化其堆栈分配(只需重新标记)内部)。我从来没有深入研究过为什么这种情况不会发生,可能有一些 rustc 没有设置的元数据,或者一些安全的极端情况阻止了 LLVM 这样做。