Create 2-dimensional arrays/vectors of variable length

Hi

I am trying to create an array of arrays where each individual array is an array of bytes of different length. Some can be two bytes while others can be four bytes.

let mut file_array = [[0u8; 2]; 4];

file_array[0] = hex!("CFFAEDFE");
file_array[1] = hex!("4D5A");

However, when using the above code, all individual arrays must be the same length and the code will not compile. How can I create arrays of different lengths?

Use a Vec?

Arrays have homogenous types, so you'll need either something like an enum to wrap the different types, or as @jhpratt suggested, something like a Vec that can hold different lengths. For such limited lengths, perhaps an ArrayVec<[u8;4]> would suit your needs. If it's all static data (or the lifetimes otherwise manageable), you could store slices.

1 Like

Hi

I tried the following (not sure if it is correct) but since the vectors can have different lengths, I cannot put a fixed length such as 4 as there will be errors if one of the vectors is not 4 bytes exactly.

let mut vectors: std::vec::Vec<std::vec::Vec<[u8; 4]>> = vec![vec![]];

My current code snippet

fn search_vectors()
{
	let mut vectors: std::vec::Vec<std::vec::Vec<u8>> = vec![vec![]];
	vectors[0].push(hex!("CFFAEDFE"));
	vectors[1].push(hex!("4D5A"));
}

There will be an error saying expected u8, found [u8; 4].

I am trying to replicate parsing of the first few bytes of various files and identifying their magic headers. I searched online and magic headers can be in different lengths, so I cannot stick to 4 bytes long.

If you're just looking to fix the error, do you have an example of something that gives that error? You can generally compare (etc) Vecs and arrays, etc, though sometimes you have to coerce things into a slice first.

If you want the type safety of explicitly only allowing 2 or 4 bytes, you'll need something like

enum FileMagic {
    Two([u8; 2]),
    Four([u8; 4]),
}

And perhaps a Deref impl to add the convenience of slices alluded to above (playground).

You need to understand that arrays of different lengths are different data types. Just like i16 and i32 are different types, [u8; 2] and [u8; 4] are different types. Therefore packing both of them into an array (your first try) does not work, because all elements in an array have to be of the same type. Packing both of them into a Vector (your second try) also does not work, because every element in a vector has to be of the same type.

You therefore have three options:

  1. Pack them both together using a technique that allows packing variables of different data types together: Using a tuple or using a struct:
struct FileArray {
	first: [u8; 4],
	second: [u8; 2],
}
fn main() {
	let file_struct = FileArray {
		first: hex!("CFFAEDFE"),
		second: hex!("4D5A"),
	};
}
  let file_tuple: ([u8; 4], [u8; 2]) = (hex!("CFFAEDFE"), hex!("4D5A"));
  1. Avoid having different dataypes: While [u8; 2] and [u8; 4] are of diffent type, vectors of different size are of the same type and you can therefore pack two vectors of different length into the same array or vector.
	let vec_1: Vec::<u8> = hex!("CFFAEDFE").to_vec();
	let vec_2: Vec::<u8> = hex!("4D5A").to_vec();
	let vec_ar: [Vec::<u8>; 2] = [vec_1, vec_2];
  1. Creating a datatype that can be both [u8; 2] or [u8; 4]:
enum FileMagic {
    Two([u8; 2]),
    Four([u8; 4]),
}
fn main() {
	let magic_ar: [FileMagic; 2] = [FileMagic::Four(hex!("CFFAEDFE")), FileMagic::Two(hex!("4D5A"))];
}

Note that the type annotations in my examples are optional.

Memory wise, those three solutions behave differently:

file_tuple stack-allocate 6 bytes (+padding).

vec_ar will stack-allocate 48 bytes (+padding) (a vec is a 24 byte stack allocation) and will heap allocate at least 4 bytes and will heap allocate at least another 2 bytes.

magic_ar will stack-allocate 10 bytes (+padding).

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.