Casting &[i32] as &[f32]

Assuming for a moment that I have a legitimate reason to do this, is there any danger to casting &[i32] <-> &[f32] ?

Context: I have this system where the user can declare new "types" at runtime, as longa s the types consists of only i32 & f32's. So they can declare the type (i32, i32), (i32, f32), (i32, f32, f32), but not the type (i32, u8, i32). My plan is to just store this as a Vec and store how many elems the 'type' contains. My question here is -- casting between &[i32] <-> &[f32] .. are there any gotchas? (Seems like there should be none, as they have the same alignment).

EDIT: It's fine if the values is just reinterpreting bits. I'm not expecting it to do an element wise "as i32" or "as f32" when I cast the slices.

I think if you're doing direct bit reinterpretation using either f32::from_bits / to_bits it should be fine. I couldn't find any documentation saying that transmute is OK, but from_bits is documented to behave identically to transmute, and it's safe.

I'd be worried about NaN if you were doing as i32, but with from/to_bits, it should be fine and there's a valid integer value which is those bits.

1 Like

Yes, it's fine. Every neither f32and i32 have any niches or uninit bytes, and they have the exact same memory layout. So this cast is always valid. But you can't use std::mem::transmute because the layout of fat pointers is not stable.

but this is fine

fn cast(slice: &[f32]) -> &[i32] {
    unsafe {
        std::slice::from_raw_parts(
            slice as *const [f32] as *const i32,
            slice.len(),
        )
    }
}
5 Likes

Slightly pedantic, is the [f32] supposed to be f32 in

slice as *const [f32] as *const i32,

?

I think so! It's an &[f32] so it needs to be cast to *const [f32] before it can be cast to another *const type. Casting cast &[f32] to *const [i32] would not be allowed.

bytemuck wraps these kind of casts safely. And can help with custom types with a couple unsafe impls.

https://crates.io/crates/bytemuck

2 Likes