Defining a "custom" UB for object-to-primitive transmutation

⚓ rust    📅 2025-05-26    👤 surdeus    👁️ 4      

surdeus

Warning

This post was published 33 days ago. The information described in this article may have changed.

A bit of context

Reading through the docs, I noticed an extremely interesting claim:

Note that it is undefined behavior to transmute from Discriminant to a primitive!
<...>

// ⚠️ This is undefined behavior. Don't do this. ⚠️
// assert_eq!(0, unsafe { std::mem::transmute::<_, u8>(std::mem::discriminant(&unit_like)) });

discriminant in std::mem - Rust

There is an unsafe block so std is totally free to define this as UB as if std::hint::unreachable_unchecked() was called... though... how is it done, exactly?

Discriminant reading can be moved out of the block, the only unsafe operation is std::mem::transmute call so it must be the one invoking UB. There seem to be two possible options which it is:

  1. [undefined.intrinsic] Invoking undefined behavior via compiler intrinsics.
    I would imagine this like "transmute may check whether the source type is Discriminant<T>, and if it is, insert a spurious unreachable_unchecked() call". Doesn't seem applicable to other ways of reinterpreting a value (like ptr::read_unaligned) though.

  2. [undefined.invalid] Producing an invalid value.
    However, the only invalid value I know for the primitive number types is uninitialized memory.
    It is certainly possible, and no trait on Discriminant<T> rules that out.

Question

Now, my question is: if one day I decided to make a structure S which would invoke immediate UB upon attempt to transmute it to primitive, what could I do? One option I see is carrying around a MaybeUninit<...> to have some uninit bytes in the struct.

2 posts - 2 participants

Read full topic

🏷️ rust_feed