Index error when copying Vec[u8] partially to ArrayVec

I'm trying to copy Vec<u8> (because data size is only known at runtime) elements into an ArrayVec (used later because of fixed data lengths).

const MAX_HASH_SIZE: usize = 32;
const ELEMENTS: usize = 11;

let mut array: ArrayVec<[ArrayVec<[u8; MAX_HASH_SIZE]>; ELEMENTS]> = Default::default();

for idx in 1..=ELEMENTS {
  let file_data: Vec<u8> = read_file(); // creates and returns Vec<u8>
  if file_data.len() != (MAX_HASH_SIZE+1) {
    panic!("file_data len is {}, should be {}", file_data.len(), (1 + MAX_HASH_SIZE));
  }
  array[(idx-1) as usize].copy_from_slice(&file_data[1..]);
  // array[(idx-1) as usize].as_mut_slice().copy_from_slice(&file_data[1..]);
}

That results in index out of bounds: the len is 0 but the index is 0 for the last statement.

I've also tried:

for idx in 1..=ELEMENTS {
  let file_data: Vec<u8> = read_file(); // creates and returns Vec<u8>
  if file_data.len() != (MAX_HASH_SIZE+1) {
    panic!("file_data len is {}, should be {}", file_data.len(), (1 + MAX_HASH_SIZE));
  }
  let mut hash_value: ArrayVec<[u8; MAX_HASH_SIZE]> = Default::default();
  hash_value.copy_from_slice(&file_data[1..]);
}

which results in source slice length (32) does not match destination slice length (0).

So other than usual, there's no compile time type error. But currently I'm a bit clueless, I don't have much experience with Arrays/slices/vectors in Rust as of yet.

what works is the following, but I think it's a bad solution because I copy once into an array and then into the ArrayVec:

    let mut node: [u8; MAX_HASH_SIZE] = [0; MAX_HASH_SIZE];
    node.copy_from_slice(&file_data[1..]);
    node_array.push(node.into());

Some help to get this running would be highly appreciated.

The problem is that you're using copy_from_slice, which is a method on slices that copies the elements of one to the other. However your array[(idx-1) as usize]/hash_array is empty, hence when viewed as a slice it has length 0, and since you're trying to copy a slice that has a length bigger than 0 into it, you get a panic. What you probably want to do instead is to use .extend(...) instead of .copy_from_slice(...) so that the new slice elements are appended to it.

The reason it works with an array is that its elements are all already initialized, so when viewed as a slice it has length MAX_HASH_SIZE and the copy works without problems.

PS: the use of ArrayVec<[u8; MAX_HASH_SIZE]> tells me you're using at most arrayvec version 0.5.2, and the latest one is 0.7.4, you should consider updating.

You don't need ArrayVec here. The inner one can be changed to a plain array with some minor changes.

let mut array: ArrayVec<[[u8; MAX_HASH_SIZE]; ELEMENTS]> = ArrayVec::new();

for _ in 0..ELEMENTS {
    let file_data: Vec<u8> = read_file();
    if file_data.len() != (MAX_HASH_SIZE + 1) {
        panic!(
            "file_data len is {}, should be {}",
            file_data.len(),
            (1 + MAX_HASH_SIZE)
        );
    }
    let a: &[u8; MAX_HASH_SIZE] = file_data[1..].try_into().unwrap();
    array.push(*a);
}

And the outer one can be a plain array by using std::array::from_fn.

let array: [[u8; MAX_HASH_SIZE]; ELEMENTS] = std::array::from_fn(|_idx| {
    let file_data: Vec<u8> = read_file();
    if file_data.len() != (MAX_HASH_SIZE + 1) {
        panic!(
            "file_data len is {}, should be {}",
            file_data.len(),
            (1 + MAX_HASH_SIZE)
        );
    }
    let a: &[u8; MAX_HASH_SIZE] = file_data[1..].try_into().unwrap();
    *a
});

They're probably using tinyvec::ArrayVec, which is the only one on playground.

Thanks for the response.

Yeah I realize now that I've treated the ArrayVec more as an array than a vector...so that explains the first index out of bounds message.

The methods extend() and extend_from_slice() so far didn't work either, because of the argument type being wrong:

  • file_data is Vec<u8> and can be cast into a slice &[u8]
  • but for adding slices to the ArrayVec of ArrayVecs via extend() methods, the slice type would need to be the inner element type &ArrayVec<[u8; MAX_HASH_SIZE]>)

I've checked Cargo.lock and can see that ArrayVec is apparently introduced via tinyvec which is at version 1.6.0. That's the current version.

Thanks -- I think this may eventually be a nice solution.

While the code example is generally working, I currently have to provide the "inner type" as ArrayVec for a library call. However, I can probably change that.