3

Use for loop on a rust array works correctly:

fn main() {
    let v = [1, 2, 3, 4, 5];
    for _ in v.into_iter() {}
    for _ in v.into_iter() {}
}

But substituting a vec doesn't compile:

fn main() {
    let v = vec![1, 2, 3, 4, 5];
    for _ in v.into_iter() {}
    for _ in v.into_iter() {}
}

The error:

use of moved value: `v`

I understand why this program does not work with vec. But why does it work with array? I was expecting a similar error in the array example, but it gives no error.

TSK
  • 509
  • 1
  • 9
  • 3
    You should see `move occurs because v has type Vec, which does not implement the Copy trait` - implying that `[i32; 5]` implements the Copy trait which is why you can use `into_iter` on it multiple times without it moving. – kelsny Oct 23 '22 at 05:05
  • @caTS If the array implements `Copy` trait, is it true that the array is copied once for each `for` loop? – TSK Oct 23 '22 at 05:31
  • 1
    As the other comment suggests, [array implements `Copy`](https://doc.rust-lang.org/std/primitive.array.html#impl-Copy-for-%5BT%3B%20N%5D) as long as the contained data type also implements `Copy`. And yes, the array is copied once for each `for` loop. – kotatsuyaki Oct 23 '22 at 05:35
  • @kotatsuyaki Does this suggest looping with `&v` is more efficient even if looping with `v` compiles (where `v` is an array), because the copy of the entire array is avoided? – TSK Oct 23 '22 at 05:46
  • @TSK The performance is likely to be the same. Even though *semantically* there are copies happening in the code, the compiler is perfectly capable of eliminating them. If in doubt, feel free to use tools like the compiler explorer to examine the assembly outputs. – kotatsuyaki Oct 23 '22 at 06:54
  • 1
    @kotatsuyaki I actually had recently a case where memcpy was a perf bottleneck and after investigation it turned out to be because of that. But usually this is indeed not a problem. Bench before changing. – Chayim Friedman Oct 23 '22 at 07:11

1 Answers1

4

Added detail:

  • Arrays are stored on the stack in Rust (like C/C++), therefore, they can be moved -- Copy() in rust -- which makes a deep copy of the data, but it's ok because stack data is supposed to be light-weight.

  • Vectors are stored on the heap in Rust (like C/C++), therefore, they usually CANNOT be moved, and must usually be deep copied.

Here is a great explanation on the difference between Copy() and Clone()

Original Response:

As another commenter mentioned, Arrays in Rust implement the Copy trait, and can therefore be passed-by-value multiple times, whereas Vector types must be explicitly clone()d to achieve the same behavior.

In Rust, when a function's parameter is pass-by-value, the compiler defaults to doing a move of a copy on all calls except the last one, on the last call it will transfer ownership of the original, instead of a copy/clone. The compiler will not automatically run clone(), if Copy isn't implemented.

Here is the doc for Copy trait: https://doc.rust-lang.org/std/marker/trait.Copy.html

Array's impl of Copy documentation can be found here: https://doc.rust-lang.org/std/primitive.array.html#impl-Copy-for-%5BT%3B%20N%5D

Here is a great article with more details: https://colinsblog.net/2021-04-16-rust-ownership-comparisons/

spdrman
  • 1,372
  • 2
  • 13
  • 18
  • There is no `.copy()`. – user3840170 Oct 23 '22 at 07:48
  • Good catch. Fixed to reflect Copy redirecting to Clone() – spdrman Oct 23 '22 at 07:59
  • Linking to the code where it is implemented might create the impression that programmers are required to look through the Rust source code to find these things out. The expected way is here, first section: https://doc.rust-lang.org/std/primitive.array.html and here: https://doc.rust-lang.org/std/primitive.array.html#impl-Copy-for-%5BT%3B%20N%5D – Finomnis Oct 23 '22 at 08:19