Looking for a way to read data from Vec<u32> without converting it into Vec<u8>

Hi community !

I have a Vec<u32> and I need to read u64, i32, u32, f32 from it. Currently to do this I use std::io::Cursor, so first of all I convert the Vec<u32> data into Vec<u8>:

let data: Vec<u8> = {
    let buffer: Vec<u32> = update_blocks.values().cloned().collect();
    buffer.iter().flat_map(|&num| num.to_le_bytes().to_vec()).collect()
};

and next I use this data in Cursor. But probably there exists some more compact way in Rust that will let me read all needed data directly from Vec<u32> ?

Could somebody suggest something more optimized ?

Use bytemuck to cast from &[u32] to &[u8].

5 Likes

Well, since all of your data types have a size that is a multiple of 4, you don't need any conversion to a sequence of u8. You can get all of your types just using built-in operations and the standard library: Playground

pub fn read_u32(slice: &mut &[u32]) -> Option<u32> {
    let &[first, ref rest @ ..] = *slice else { return None };
    *slice = rest;
    Some(first)
}

pub fn read_i32(slice: &mut &[u32]) -> Option<i32> {
    let &[first, ref rest @ ..] = *slice else { return None };
    *slice = rest;
    Some(first as i32)
}

pub fn read_f32(slice: &mut &[u32]) -> Option<f32> {
    let &[first, ref rest @ ..] = *slice else { return None };
    *slice = rest;
    Some(f32::from_bits(first))
}

pub fn read_u64(slice: &mut &[u32]) -> Option<u64> {
    let &[lo, hi, ref rest @ ..] = *slice else { return None };
    *slice = rest;
    Some(u64::from(lo) | (u64::from(hi) << 32))
}

Anyway, your original code is full of useless allocation and copying. You could just do

let data: Vec<u8> = update_blocks
    .values()
    .copied()
    .flat_map(u32::to_le_bytes)
    .collect();

and still be more efficient than what you currently have.


Furthermore, the call to update_blocks.values() suggests that you don't actually have a Vec<u32> to begin with, but you have a HashMap or BTreeMap where the values are u32. In this case, you don't need to create a slice at all. Just use the returned values iterator and call .next() on it, mapping the appropriate conversion over the returned Option<u32>: Playground

let value: u32 = iter.next()?;
let value: i32 = iter.next()? as i32;
let value: u64 = {
    let lo = iter.next()?;
    let hi = iter.next()?;
    u64::from(lo) | (u64::from(hi) << 32)
};
let value: f32 = iter.next()?.from_bits();
7 Likes

Thank you very much for detailed explanation. I like your solution.