Vec<f32> to &[u8]

EDIT: Sorry for not being clear. I am passing this Vec to OpenGL. I want to keep the same bit pattern and just get a pointer to it.

I have a piece of rust code which gives me:

   = note: expected type `&[u8]`
              found type `std::vec::Vec<f32>`

I know precisely why this happens, the arg passed as y indeed is a Vec.

Now, my question: Vec to &[u8] seems like a common op. How do I make this conversion?

You have a collection of 32-bit floating-point numbers. You need a slice of 8-bit unsigned integers.

Do you want to convert each f32 into a u8 individually, by rounding it? If so, what should happen to numbers that are less than 0 or greater than 255?

Or do you want to reinterpret the memory from the Vec<f32> as a sequence of bytes, so that (for example) a vector of three numbers would become a slice of twelve bytes?

The answers depend on what you are ultimately trying to do that needs an &[u8].

Sorry for the confusion. I am passing this Vec to OpenGL.

I do not want to do any conversion. I want to keep the same bits and just get it as a &[u8]. This is equiv to a C

(unsigned char*) float_array

You can use slice::from_raw_parts:

std::slice::from_raw_parts(vec.as_ptr(), vec.len() * 4)
std::slice::from_raw_parts(vec.as_ptr(), vec.len() * 4)

almost works. We actually need:

std::slice::from_raw_parts(vec.as_ptr() as *const u8, vec.len() * 4)

right?

1 Like

Oops! You are right.

You may already be aware, but just to state explicitly: This method loses lifetime information. So, the following code would compile just fine even though it violates memory safety:

fn main() {
    let my_nums = vec![1f32, 2., 3., 4.];
    
    let my_nums_unstructured: &[u8] = unsafe {
        std::slice::from_raw_parts(my_nums.as_ptr() as *const u8, my_nums.len() * 4)
    };
    
    std::mem::drop(my_nums);
    
    println!("{:?}", my_nums_unstructured);
}

You may want to define a function like this:

fn to_byte_slice<'a>(floats: &'a [f32]) -> &'a [u8] {
    unsafe {
        std::slice::from_raw_parts(floats.as_ptr() as *const _, floats.len() * 4)
    }
}

This preserves the lifetimes so that the following code (correctly) does not compile:

fn main() {
    let my_nums = vec![1f32, 2., 3., 4.];
    
    let my_nums_unstructured = to_byte_slice(&my_nums[..]);
    
    // COMPILE ERROR: error[E0505]: cannot move out of `my_nums` because it is borrowed
    std::mem::drop(my_nums);
    
    println!("{:?}", my_nums_unstructured);
}
3 Likes

Interesting. I did not consider lifetime issues. The code I have so far is:

fn vf_to_u8(v: &Vec<f32>) -> &[u8] {
    unsafe { std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * 4) }
}

Do I need an explicit 'a ?

An implicit lifetime has the same effect. The problem for using from_raw_parts directly is that it will happily infer the output lifetime to anything, even 'static. With a function wrapping this, the input and output lifetimes are tied together, regardless of the inner code.

1 Like

Btw, you can just write:

Since taking a value of type &Vec<_> rather than &[_] is almost always pointless.

3 Likes