Reading AIFF file - How do I create a loop that generates a vector/array of buffers

I have a program that reads an AIFF file and processes each chunk. It works. I currently have a number of lines that create buffers (variables raw_bytes 1..4). Each buffer is used to read data from an AIFF file and in turn a reference to each buffer is used in a for loop within scoped threads. What I would like to be able to do is control the number of threads utilized simply by changing the variable number_threads. To do so I need to be able to programmatically create a vector/array that contains references to buffers based on the number of threads indicated. For some reason I am drawing a blank. Below is the code. Any insight to a path forward will be appreciated.

            b"SSND" => {
                println!("----- SSND Chunk -----");
                let mut ck_data = [0u8; 8];
                file.read_exact(&mut ck_data)?;
                let offset = u32::from_be_bytes(ck_data[0..4].try_into().unwrap_or_default());
                let _block_size = u32::from_be_bytes(ck_data[4..8].try_into().unwrap_or_default());
                if offset > 0 {
                    file.seek(SeekFrom::Current(offset as i64))?;
                }

                let number_threads = 4;
                let total_sound_bytes = ck_size - 8 - offset;
                let fraction_of_sound_bytes= total_sound_bytes / number_threads;
                let total_samples = total_sound_bytes / 2;
                let total_samples_per_channel = total_samples /2;
                let fraction_of_channel_samples = total_samples_per_channel / number_threads;

// ------------------------------------------------------------------------------------------------
// convert this section of code to a loop that is based on variable above number_threads

                let mut raw_bytes1 = vec![0u8; fraction_of_sound_bytes as usize];
                file.read_exact(&mut raw_bytes1)?;
                let mut raw_bytes2 = vec![0u8; fraction_of_sound_bytes as usize];
                file.read_exact(&mut raw_bytes2)?;
                let mut raw_bytes3 = vec![0u8; fraction_of_sound_bytes as usize];
                file.read_exact(&mut raw_bytes3)?;
                let mut raw_bytes4 = vec![0u8; fraction_of_sound_bytes as usize];
                file.read_exact(&mut raw_bytes4)?;

                let raw_bytes = [&raw_bytes1, &raw_bytes2, &raw_bytes3, &raw_bytes4];
// ------------------------------------------------------------------------------------------------

                let mut left_channel_samples: Vec<i16> = Vec::with_capacity(total_samples_per_channel as usize);
                let mut right_channel_samples: Vec<i16> = Vec::with_capacity(total_samples_per_channel as usize);

                thread::scope(|s| {

                    let mut handles = Vec::with_capacity(4);

                    for bytes in raw_bytes {
                        let handle = s.spawn(|| {
                            let mut left_chnl = Vec::with_capacity(fraction_of_channel_samples as usize);
                            let mut right_chnl = Vec::with_capacity(fraction_of_channel_samples as usize);
                            let mut left_sample: i16;
                            let mut right_sample: i16;
                            for chunk in bytes.chunks_exact(4) {
                                left_sample = i16::from_be_bytes(chunk[0..2].try_into().unwrap_or_default());
                                right_sample = i16::from_be_bytes(chunk[2..4].try_into().unwrap_or_default());
                                left_chnl.push(left_sample);
                                right_chnl.push(right_sample);
                            } // end for loop
                            (left_chnl, right_chnl)
                        }); // end of let handle
                        handles.push(handle);
                    } // end for loop

                    for handle in handles {
                        if let Ok(channels) = handle.join() {
                            left_channel_samples.extend(channels.0);
                            right_channel_samples.extend(channels.1);
                        }; // end if let
                    } // end for loop
                });  // end thread scope

                println!("left channel sample len = {}", left_channel_samples.len());
                println!("left channel capacity = {}", left_channel_samples.capacity());
                println!("right channel sample len = {}", right_channel_samples.len());
                println!("right channel capacity = {}", right_channel_samples.capacity());

            },  // end SSND ###################################################

Just declare raw_bytes as a Vec, and push each raw_bytes{0..number_threads} within the loop. Like this:

            // ------------------------------------------------------------------------------------------------
            // convert this section of code to a loop that is based on variable above number_threads
-            let mut raw_bytes1 = vec![0u8; fraction_of_sound_bytes as usize];
-            file.read_exact(&mut raw_bytes1)?;
-            let mut raw_bytes2 = vec![0u8; fraction_of_sound_bytes as usize];
-            file.read_exact(&mut raw_bytes2)?;
-            let mut raw_bytes3 = vec![0u8; fraction_of_sound_bytes as usize];
-            file.read_exact(&mut raw_bytes3)?;
-            let mut raw_bytes4 = vec![0u8; fraction_of_sound_bytes as usize];
-            file.read_exact(&mut raw_bytes4)?;
-
-            let raw_bytes = [&raw_bytes1, &raw_bytes2, &raw_bytes3, &raw_bytes4];
+            let mut raw_bytes = vec![];
+
+            for _ in 0..number_threads {
+                let mut raw_bytes_i = vec![0u8; fraction_of_sound_bytes as usize];
+                file.read_exact(&mut raw_bytes_i)?;
+                raw_bytes.push(raw_bytes_i);
+            }
            // ------------------------------------------------------------------------------------------------

            let mut left_channel_samples: Vec<i16> = Vec::with_capacity(total_samples_per_channel as usize);
            let mut right_channel_samples: Vec<i16> = Vec::with_capacity(total_samples_per_channel as usize);

            thread::scope(|s| {
-                let mut handles = Vec::with_capacity(4);
+                let mut handles = Vec::with_capacity(raw_bytes.len());
-                for bytes in raw_bytes {
+                for bytes in raw_bytes.iter() {
                    let handle = s.spawn(|| {

Note this results in raw_bytes being of type Vec<Vec<u8>> rather than Vec<&Vec<u8>>. It's impossible to declare separate variables raw_bytes{1..number_threads} outside the loop, as number_threads is a runtime value, while the number of named variables must be fixed at compile time. Therefore, we store the inner vectors as owned values and borrow them via .iter() during iteration.

Thank you for the response and excellent description. I do have one question. I used your code and I get the right output. I attempted to allocate the size of the vector and it ends up giving me length and capacity that are twice too big for both the left and right channels. I do not understand why the allocation does not work. Below is the code and output. Any insight will be appreciated.

                // let mut raw_bytes = vec![vec![0u8; fraction_of_sound_bytes as usize]; number_threads as usize];
                // println!("----- raw bytes capacity = {}", raw_bytes.capacity());
                let mut raw_bytes = vec![];
                for _ in 0..number_threads {
                    let mut thread_bytes = vec![0u8; fraction_of_sound_bytes as usize];
                    println!("----- thread bytes = {}", thread_bytes.len());
                    file.read_exact(&mut thread_bytes)?;
                    raw_bytes.push(thread_bytes);
                }


vec![] not allocated as in your example
----- SSND Chunk -----
total sound bytes = 64585920
----- thread bytes = 16146480
----- thread bytes = 16146480
----- thread bytes = 16146480
----- thread bytes = 16146480
----- left chanl sample capacity = 16146480
left channel sample len = 16146480
left channel capacity = 16146480
right channel sample len = 16146480
right channel capacity = 16146480

vec![...] with attempt to allocate size
----- SSND Chunk -----
total sound bytes = 64585920
----- raw bytes capacity = 4
----- thread bytes = 16146480
----- thread bytes = 16146480
----- thread bytes = 16146480
----- thread bytes = 16146480
----- left chanl sample capacity = 16146480
left channel sample len = 32292960
left channel capacity = 32292960
right channel sample len = 32292960
right channel capacity = 32292960


When reading bytes, we should use the prepared Vec instead of pushing a new one. Like this:

+let mut raw_bytes = vec![vec![0u8; fraction_of_sound_bytes as usize]; number_threads as usize];
// println!("----- raw bytes capacity = {}", raw_bytes.capacity());
-let mut raw_bytes = vec![];
-for _ in 0..number_threads {
+for i in 0..number_threads {
-    let mut thread_bytes = vec![0u8; fraction_of_sound_bytes as usize];
-    println!("----- thread bytes = {}", thread_bytes.len());
-    file.read_exact(&mut thread_bytes)?;
+    file.read_exact(&mut raw_bytes[i as usize])?;
-    raw_bytes.push(thread_bytes);
}