23

这里有一个很好的 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()的堆栈帧?

感谢您在解释这个难题时提供任何帮助。

4

1 回答 1

17

它物理上是否移动到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并将原始指针保存到该堆分配的内存。注意Boxdirectly(里面的原始指针)是直接在栈上,而不是在堆上!只是在i32堆上。

正如我刚才所说,当Box它被移动时,它被编辑。memcpy这意味着堆栈内容被复制(!!)......因此只是指针被逐字节复制。没有第二个版本i32

在身体移动方面,类型Copy和非类型之间没有区别。Copy唯一的区别是编译器对这些类型执行不同的规则。

于 2016-03-26T02:05:44.327 回答