Rust如何移动不可复制的堆栈变量?

23

这里有一个很好的Rust移动语义示例: Rust Move Semantics在Rust By Example网站上有记录。

我基本理解了所示范的两种情况。第一种是如何给基本类型创建一个新的别名,因为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()”的堆栈帧中物理移动?任何关于解释这个谜题的帮助都将不胜感激。

2
你介意简化一下你的例子吗?将Employee结构体简化并且至少移除生命周期会更好。比如说, struct Employee { age: i32 }就足够了。 - Lukas Kalbertodt
@LukasKalbertodt - 是的,我已经简化了这个例子。 - Ralph Caraveo
链接已更改:https://doc.rust-lang.org/rust-by-example/scope/move.html - Amit Erandole
1个回答

17

它会被物理地移动到do_something()的堆栈帧中吗?

是的。非Copy类型的物理移动方式与Copy类型完全相同:使用memcpy。你已经了解到原始的Copy类型是逐字节复制到新位置(例如新的堆栈帧)中的。

现在考虑这个Box的实现:

struct Box<T> {
    ptr: *const T,
}

When you have

let b = Box::new(27i32);
do_something(b);    // `b` is moved into `do_something`

当分配一个在堆上时,Box 会保存指向该堆内存的原始指针。请注意,Box 直接(内部的原始指针)位于栈上,而不是堆上!只有在堆上。

移动 Box 时,它被复制,就像我刚才所说的。这意味着栈内容被复制了(!!)... 因此仅指针以字节为单位被复制。并没有第二个版本的 !

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


11
小细节:据我所知,不能保证这些值会被实际移动,但其语义使你必须假设它们被移动了。例如,编译器可以自由地重写do_something以接受对b的引用,以防止复制位。重要的是,代码的行为好像这些位被移动了。 - Shepmaster
那么,也许实际的内部区别在于,对于“Copy”类型,当函数返回时没有其他事情要做,因为先前的值仍然可用,但是对于非“Copy”类型,堆栈应该被“修复”,因为先前(移动的)值仍然会挂在那里,对吧?那么这个内存什么时候被释放?堆栈是否以某种方式被压缩,或者该值是否永远停留在那里(直到另一个函数返回)? - rsalmei
@rsalmei 我不完全确定我理解你的问题。无论是 Copy 还是不是,堆栈内存在函数返回时都会被释放。对于实现了 Drop 的类型,Rust 保证在正确的位置调用 drop 一次。为了做到这一点,Rust 有时必须将一些位存储在堆栈中,记住某些东西是否已经移动。请注意,Copy 类型不能实现 Drop。最后,我不确定你所说的“固定”是什么意思。 - Lukas Kalbertodt
太好了,谢谢@LukasKalbertodt!我所说的“fixed”是指压缩、碎片整理、删除之前函数调用移动的值。但你回答说Rust存储了额外的标志,告诉内存是否可用。因此,该内存会一直存在,直到该函数结束。 - rsalmei

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