10

If I have two arrays of different sizes:

let mut array1 = [0; 8];
let array2 = [1, 2, 3, 4];

How would I copy array2 into the first 4 bytes of array1? I can take a mutable 4 byte slice of array1, but I'm not sure how or if I can assign into it.

malbarbo
  • 10,717
  • 1
  • 42
  • 57
Paul D
  • 293
  • 1
  • 2
  • 5

3 Answers3

12

Manually one can do

for (dst, src) in array1.iter_mut().zip(&array2) {
    *dst = *src
}

for a typical slice. However, there is a likely faster specialization in clone_from_slice:

dst[..4].clone_from_slice(&src)

A slightly older method is to use std::io::Write, which was implemented for &mut [u8].

use std::io::Write;
let _ = dst.write(&src)

This will write up to the end of dst and return how many values were written in a Result. If you use write_all, this will return an Err if not all bytes could be written.

Veedrac
  • 58,273
  • 15
  • 112
  • 169
8

The most flexible way is to use iterators to handle each element successively:

for (place, data) in array1.iter_mut().zip(array2.iter()) {
    *place = *data
}

.mut_iter creates an Iterator that yields &mut u8, that is, mutable references pointing into the slice/array. iter does the same but with shared references. .zip takes two iterators and steps over them in lock-step, yielding the elements from both as a tuple (and stops as soon as either one stops).

If you need/want to do anything 'fancy' with the data before writing to place this is the approach to use.

However, the plain copying functionality is also provided as single methods,

  • .copy_from, used like array1.copy_from(array2).

  • std::slice::bytes::copy_memory, although you will need to trim the two arrays because copy_memory requires they are the same length:

    use std::cmp;
    use std::slice::bytes;
    
    let len = cmp::min(array1.len(), array2.len());
    bytes::copy_memory(array1.mut_slice_to(len), array2.slice_to(len));
    

    (If you know that array1 is always longer than array2 then bytes::copy_memory(array1.mut_slice_to(array2.len()), array2) should also work.)

At the moment, the bytes version optimises the best, down to a memcpy call, but hopefully rustc/LLVM improvements will eventually take them all to that.

Peter Burns
  • 44,401
  • 7
  • 38
  • 56
huon
  • 94,605
  • 21
  • 231
  • 225
6

You could simply use copy_from_slice() and use Range & Co:

fn main() {
    let mut dest = [0; 8];
    let src = [1, 2, 3, 4];

    dest[..4].copy_from_slice(&src);

    assert_eq!(dest, [1, 2, 3, 4, 0, 0, 0, 0]);
}

Inverse case:

fn main() {
    let src = [1, 2, 3, 4, 5, 6, 7, 8];
    let mut dest = [0; 4];

    dest.copy_from_slice(&src[2..6]);

    assert_eq!(dest, [3, 4 ,5, 6]);
}

Combined case:

fn main() {
    let src = [1, 2, 3, 4, 5, 6, 7, 8];
    let mut dest = [0; 4];

    dest[1..3].copy_from_slice(&src[3..5]);

    assert_eq!(dest, [0, 4, 5, 0]);
}
Stargateur
  • 24,473
  • 8
  • 65
  • 91
  • How could I modify your first example to fill the destination array with the `src` array repeatedly in the most idiomatic / fastest way? e.g. `assert_eq!(dest, [1, 2, 3, 4, 1, 2, 3, 4]);` At first glance it may look like one would want that behavior to panic because there's no way to know if `src` fits within `dest` but presumably one could just check if the lengths are divisible – airstrike Jun 28 '23 at 12:59
  • 1
    @airstrike see other answer with `.zip()` and use `repeat()` – Stargateur Jun 28 '23 at 21:02