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].
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);
}
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);
}
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.