1

When the array is returned (moved) from the get_array() function, are the elements being copied?

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);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
quentinadam
  • 3,058
  • 2
  • 27
  • 42
  • In this example, they would be copied. As far as I know all arrays are copied on move. However, I only have a rough understanding of move semantics so there may be some exceptions. – Locke Nov 23 '21 at 18:18
  • 3
    The question is under-specified. Sometimes the data will be copied, other times it won't, depending on optimization. As a trivial example, `get_array` may be inlined and the assignment to `b` is seen as useless, thus no copy is needed. – Shepmaster Nov 23 '21 at 18:25
  • See also [Can Rust optimise away the bit-wise copy during move of an object someday?](https://stackoverflow.com/q/38571270/155423); [Why does "move" in Rust not actually move?](https://stackoverflow.com/q/53465843/155423) – Shepmaster Nov 23 '21 at 18:25
  • 1
    In a hypothetical land with no optimization, then yes, it's being copied. But, we don't live in that hypothetical land, and so there are many optimizations that can be made. In this case, `get_array()` will be replaced with `[0, 1, 2, 3]` at compile-time, and in other cases the array might be constructed directly in the parent function's stack frame to avoid unnecessary copies. Of course, sometimes a copy is unavoidable. – Aplet123 Nov 23 '21 at 19:50
  • 5
    *Every* move is conceptually a (trivial, bitwise) copy. The only difference between move and copy is that move leaves the original inaccessible and prevents it from being dropped. As others have pointed out, the actual copying can be eliminated by the optimizer when it can prove that it is safe to do so. – user4815162342 Nov 23 '21 at 20:47

1 Answers1

3

You should write your program as if all moves act as destructive bitwise copies. However, this is not always what happens, various optimizations can change this behaviour when there's no need to actually copy. For example:

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

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

In this example, (conceptually) x in the function bar() is moved into x in the function foo() when bar() returns.

But in reality, if the function is inlined, no move happens at all. If you're curious about what is happening, check out godbolt.org to view the emitted assembly.

Also, as always, if your concern is a performance reason, give it a try first and benchmark, you might be surprised by some of the optimizations rustc/LLVM are able to perform.

Flavio Vilante
  • 5,131
  • 1
  • 11
  • 15
cameron1024
  • 9,083
  • 2
  • 16
  • 36