Warning
This post was published 33 days ago. The information described in this article may have changed.
Reading through the docs, I noticed an extremely interesting claim:
Note that it is undefined behavior to
transmute
fromDiscriminant
to a primitive!
<...>// ⚠️ This is undefined behavior. Don't do this. ⚠️ // assert_eq!(0, unsafe { std::mem::transmute::<_, u8>(std::mem::discriminant(&unit_like)) });
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:
[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.
[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.
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
🏷️ rust_feed