【问题标题】:Are arrays copied on move?数组是在移动中复制的吗?
【发布时间】:2022-01-02 05:42:31
【问题描述】:

get_array() 函数返回(移动)数组时,是否复制了元素?

fn get_array() -> [u8; 4] {
    let a = [0, 1, 2, 3];
    let mut b = a; // a move here produces the array to be copied over
    b[2] = 10;
    a
}

fn main() {
    let a = get_array();
    println!("{:?}", a);
}

【问题讨论】:

  • 在本例中,它们将被复制。据我所知,所有数组都是在移动时复制的。不过,我对移动语义只有粗略的了解,所以可能会有一些例外。
  • 问题未指定。有时数据会被复制,有时则不会,这取决于优化。举个简单的例子,get_array 可以被内联,分配给b 被认为是无用的,因此不需要复制。
  • 在没有优化的假设土地上,是的,它正在被复制。但是,我们并不生活在那个假设的土地上,因此可以进行许多优化。在这种情况下,get_array() 将在编译时替换为[0, 1, 2, 3],而在其他情况下,数组可能会直接在父函数的堆栈帧中构造,以避免不必要的复制。当然,有时副本是不可避免的。
  • 每个 移动在概念上都是(微不足道的,按位)复制。 move 和 copy 之间的唯一区别是 move 使原始文件无法访问并防止其被丢弃。正如其他人指出的那样,当优化器可以证明这样做是安全的时,可以消除实际的复制。

标签: arrays rust move-semantics


【解决方案1】:

您应该编写您的程序,就好像所有的移动都是破坏性的按位复制。然而,这并不总是发生,当不需要实际复制时,各种优化可以改变这种行为。例如:

fn foo() {
  let x = bar();
  println!("{}", x);
}

fn bar() -> [u8, 1024] {
  let x = [1; 1024];
  x
}

在本例中,(概念上)bar() 函数中的xbar() 返回时移动到函数foo() 中的x

但实际上,如果函数是内联的,则根本不会发生任何移动。如果您对正在发生的事情感到好奇,请查看 godbolt.org 以查看发出的程序集。

此外,与往常一样,如果您担心性能原因,请先尝试一下并进行基准测试,您可能会对 rustc/LLVM 能够执行的一些优化感到惊讶。

【讨论】:

    猜你喜欢
    • 2017-10-23
    • 2015-03-15
    • 2020-09-04
    • 1970-01-01
    • 1970-01-01
    • 2021-06-05
    • 2011-07-08
    • 2016-08-17
    • 2012-06-10
    相关资源
    最近更新 更多