Can I transmute between types?

Do any of the following cause UB:

mem::transmute::<[u8; 4], [u16; 2]>(..);
mem::transmute::<[f32; 2], f64>(..);

I would have thought not, but I've got it wrong before, especially around mem::uninitialized.

I believe these are both safe, because:

  1. None of these types have any invalid bit patterns, so the result can't be an invalid value.
  2. The input types do not contain padding, so there's no risk of reading uninitialized memory.
  3. None of the types contain pointers, so there is no risk of incorrect alignment.
2 Likes

Generally speaking, I believe the answer is yes, but the results may depend on the individual platform you are compiling for. I would tend to prefer byteorder for the first operation. I'm not sure why you want to convert in the second case, though. It seems like the results are unlikely to be desirable. However, f64::from_bits(a[0].to_bits() as u64 | a[1].to_bits() as u64) might be clearer (I think it's equivalent).

Signalling NaN?

Relative to the topic of this thread: " Can I transmute between types?",

I don't believe either can cause UB. However, successful transmute requires more than simply being non-UB. Each of these transmutes may cause an alignment fault on processors where u16 and f64 have more restrictive alignment requirements than u8 and f32, (e.g., when the [u8; 4] starts on an odd byte boundary, or the [f32; 2] starts on a 4 (mod 8) boundary).

Signaling NaN in Rust has platform-dependent behavior, but not undefined behavior. f64::from_bits can create signaling NaN in safe Rust. Further discussion.

1 Like

This would be true if the code used a raw pointer cast, but transmute takes its argument by value and returns a new value. The return value doesn't share the address of the argument value, so transmute can work without doing any unaligned reads or writes -- at least since the fix in #38670.

1 Like

There's a missing shift operation here.

2 Likes

Sorry. You are of course correct. I tend to conceptualize transmute as reinterpreting an item in memory, rather than reinterpreting an expression value with no storage connotations.

1 Like

As transmute's docs say:

it's best to think of it like a (value) memcpy.

1 Like