55

In Go, copying slices is standard-fare and looks like this:

# It will figure out the details to match slice sizes
dst = copy(dst[n:], src[:m])

In Rust, I couldn't find a similar method as replacement. Something I came up with looks like this:

fn copy_slice(dst: &mut [u8], src: &[u8]) -> usize {
    let mut c = 0;
    for (&mut d, &s) in dst.iter_mut().zip(src.iter()) {
        d = s;
        c += 1;
    }
    c
}

Unfortunately, I get this compile-error that I am unable to solve:

error[E0384]: re-assignment of immutable variable `d`
 --> src/main.rs:4:9
  |
3 |     for (&mut d, &s) in dst.iter_mut().zip(src.iter()) {
  |               - first assignment to `d`
4 |         d = s;
  |         ^^^^^ re-assignment of immutable variable

How can I set d? Is there a better way to copy a slice?

hellow
  • 12,430
  • 7
  • 56
  • 79
Byron
  • 3,908
  • 3
  • 26
  • 35
  • Could you expand a bit on why you want to copy data in slices around? I'd usually expect to either just take a reference to the original *or* to be copying the data to something that owns the copy. – Shepmaster Jan 29 '15 at 16:53

3 Answers3

95

Yes, use the method clone_from_slice(), it is generic over any element type that implements Clone.

fn main() {
    let mut x = vec![0; 8];
    let y = [1, 2, 3];
    x[..3].clone_from_slice(&y);
    println!("{:?}", x);
    // Output:
    // [1, 2, 3, 0, 0, 0, 0, 0]
}

The destination x is either a &mut [T] slice, or anything that derefs to that, like a mutable Vec<T> vector. You need to slice the destination and source so that their lengths match.


As of Rust 1.9, you can also use copy_from_slice(). This works the same way but uses the Copy trait instead of Clone, and is a direct wrapper of memcpy. The compiler can optimize clone_from_slice to be equivalent to copy_from_slice when applicable, but it can still be useful.

bluss
  • 12,472
  • 1
  • 49
  • 48
  • Thank you ! This was what I was initially looking for, but couldn't find as I was searching for anything related to `copy`. In rust-speak, clone might be the appropriate term though ... I am still getting used to it all. – Byron Jan 30 '15 at 07:33
6

This code works, even though I am not sure if it the best way to do it.

fn copy_slice(dst: &mut [u8], src: &[u8]) -> usize {
    let mut c = 0;
    for (d, s) in dst.iter_mut().zip(src.iter()) {
        *d = *s;
        c += 1;
    }
    c 
}

Apparently not specifying access permissions explicitly did the trick. However, I am still confused about this and my mental model doesn't yet cover what's truly going on there. My solutions are mostly trial and error when it comes to these things, and I'd rather like to truly understand instead.

Byron
  • 3,908
  • 3
  • 26
  • 35
  • 3
    Your approach is correct. Your initial version didn't work because dereference patterns (like `&mut d`) creates a new variable (`d` in this case) and assigns it to a dereference of the pointer. It does not provide an ability to modify the original value, it just copies the original value; if you used it with non-`Copy` type, your program won't even compile. – Vladimir Matveev Jan 29 '15 at 17:03
  • 1
    BTW, you can also keep `&s` in place and write `*d = s` afterwards. – Vladimir Matveev Jan 29 '15 at 17:03
-1

Another variant would be

fn copy_slice(dst: &mut [u8], src: &[u8]) -> usize {
    dst.iter_mut().zip(src).map(|(x, y)| *x = *y).count()
}

Note that you have to use count in this case, since len would use the ExactSizeIterator shortcut and thus never call next, resulting in a no-op.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Yasammez
  • 1,383
  • 12
  • 15
  • 3
    This is not idiomatic Rust. Iterators should generally not have side-effects, and the usage of `map` here doubly non-idomatic. If that's what you want, a `for` loop is the correct choice. – Shepmaster Mar 25 '17 at 23:44
  • 1
    I admit this is kind of a hack, but the `for` loop requires manual tracking of the number of iterations. Instead of `map` one could probably use `inspect` to make it clearer that the „result“ will get discarded. – Yasammez Mar 26 '17 at 00:52
  • @Shepmaster You may want to consider the following example that's included in the official Rust docs for `collect()`, which shows the following: `let hello: String = chars.iter().map(|&x| x as u8).map(|x| (x + 1) as char).collect();` – code_dredd Mar 24 '20 at 19:27
  • @code_dredd thanks, but this isn't my answer — I'm only an editor here. – Shepmaster Mar 24 '20 at 19:29
  • @Shepmaster No worries. I'm not defending this implementation either, or anything. I just wanted to note the detail w.r.t. the lack of side-effects for iterators and/or maps. – code_dredd Mar 24 '20 at 19:37
  • @code_dredd oh, I see what you are saying now (I thought you were saying to put that example in the answer). The example you cite does not have side effects — it only operates on the values provided to the closures and those values are not mutable references. – Shepmaster Mar 24 '20 at 19:45
  • 1
    Just as an extension on Shepmaster's first comment, here's a quote from the Rust reference on the Iterator trait: `map() is conceptually similar to a for loop. However, as map() is lazy, it is best used when you're already working with other iterators. If you're doing some sort of looping for a side effect, it's considered more idiomatic to use for than map().`. – Eliatiscom Mar 19 '21 at 12:30