这里记录了Rust的移动语义的一个很好的例子:Rust by Example网站上的Rust Move Semantics。
我对两个案例都有基本的了解。第一个原语是如何使用原始别名和原始别名仍然可以使用,因为最终结果是副本,因为i32
使用Copy
特征。这对我来说很有意义。
此外,由于许多原因,第二个示例在具有多个引用堆上i32
的别名方面是有意义的。 Rust强制执行所有权规则,因此现在无法使用原始别名创建新绑定。这有助于防止数据争用,双重释放等。
但似乎还有第三个案例没有被谈到。 Rust如何实现不实现Copy
特征的堆栈分配结构的移动?这用以下代码说明:
#[derive(Debug)]
struct Employee{
age: i32,
}
fn do_something(m: Employee){
println!("{:?}", m);
}
fn main() {
let x = Employee {
age: 25,
};
do_something(x);
//compiler error below because x has moved
do_something(x);
}
我知道:在上面的例子中, Rust会在堆栈上分配Employee
。上述结构未实现Copy
特征,因此在分配给新别名时不会被复制。这对我来说非常混乱,因为如果在堆栈上分配了Employee
结构,并且没有实现Copy
特征在哪里/如何移动?它是否实际移动到do_something()
的堆栈帧?
在解释这个难题时,任何帮助都会受到赞赏。
答案 0 :(得分:9)
它是否实际移动到
do_something()
的堆栈帧?
是。非Copy
类型的物理移动方式与Copy
类型完全相同:使用memcpy
。您已经了解原始Copy
- 类型被逐字节复制到新位置(例如新的堆栈帧)。
现在考虑Box
的实现:
struct Box<T> {
ptr: *const T,
}
当你有
时let b = Box::new(27i32);
do_something(b); // `b` is moved into `do_something`
然后在堆上分配i32
,Box
将原始指针保存到堆分配的内存中。请注意,Box
直接(内部的原始指针)直接在堆栈上,而不是在堆上!只有i32
在堆上。
移动Box
时,正如我刚才所说的那样memcpy
。这意味着堆栈内容被复制(!!)...因此只需逐个字节地复制指针。没有i32
的第二个版本!
在物理移动方面,Copy
和非Copy
类型之间没有区别。唯一的区别是编译器对这些类型强制执行不同的规则。