为什么Rust中的"移动"实际上并没有移动?

Yan*_*eao 7 rust

在下面的例子中:

struct Foo {
    a: [u64; 100000],
}

fn foo(mut f: Foo) -> Foo {
    f.a[0] = 99999;
    f.a[1] = 99999;
    println!("{:?}", &mut f as *mut Foo);

    for i in 0..f.a[0] {
        f.a[i as usize] = 21444;
    }

    return f;
}
fn main(){
    let mut f = Foo {
        a:[0;100000]
    };

    println!("{:?}", &mut f as *mut Foo);
    f = foo(f);
    println!("{:?}", &mut f as *mut Foo);
}
Run Code Online (Sandbox Code Playgroud)

我发现在传入函数之前和之后foo,地址f是不同的.为什么Rust会在任何地方复制这么大的结构但实际上不会移动它(或实现这种优化)?

我理解堆栈内存是如何工作的.但是根据Rust所有权提供的信息,我认为可以避免副本.编译器不必要地复制数组两次.这可以是Rust编译器的优化吗?

Seb*_*edl 10

移动是一个 memcpy,然后将源视为不存在。

你的大数组在堆栈上。这就是 Rust 的内存模型的工作方式:局部变量在堆栈上。由于foo函数返回时的堆栈空间将消失,因此编译器除了将内存复制到main的堆栈空间外,别无他法。

在某些情况下,编译器可以重新排列事物,以便可以省略移动(源和目标合并为一个事物),但这是不能依赖的优化,尤其是对于大事物。

如果您不想复制庞大的数组,请自己在堆上分配它,通过 aBox<[u64]>或简单地使用Vec<u64>.

  • 或者向函数传递一个 `&amp;mut f` 并且不返回任何内容,这在这种情况下是惯用的。 (5认同)
  • 在这种特殊情况下,实际上可以避免移动。变量`f`是在`main()`的栈帧中创建的,编译器可以静态地确定没有必要将它移到`foo()`的栈帧中,因为它将被复制回它的原来的位置反正。但是即使在带有标记为“#[inline(always)]”的“foo()”的发布版本中,编译器仍然不必要地复制数组两次。 (2认同)
  • @YangKeao 我想这些优化并不意味着保证。如果您想保证不发生复制,请改为传递框的引用。 (2认同)