Transmute `fn` that takes `#[repr(transparent)]`

Related: Transmuting types with repr(transparent) parameters

Is it legal (not working right now) to transmute a fn that takes a struct that is #[repr(transparent) of something to a fn that takes this something?


type TypeA = *mut c_void;

struct TypeB(TypeA);

type FnA = extern "C" fn(TypeA);
type FnB = extern "C" fn(TypeB);

let f: FnB;
let f2: FnA = unsafe { std::mem::transmute(f) }; // Is this legal?

Edit: My functions are extern "C" so a stable ABI is guaranteed.

This sounds quite similar to another issue raised against the Unsafe Code Working Group repo about transmuting a function that never returns.

Maybe the discussion there will help answer your question.

Not exactly, as my case is easier and also my functions are extern "C", so a stable ABI is guaranteed (updated the question).

@chrefr yes, and it is precisely the point of using #[repr(transparent)] over #[repr(C)]: it is valid to reinterpret one function signature as the other, but it may not be safe: for the conversion to be safe, you need to make sure the function is only fed a parameter which was safe (and a fortiori, valid) to transmute to the other type.

That is, given #[repr(transparent)] struct B(A); (or vice versa),

then transmuting an extern "ABI" fn(A, OtherArg) -> Ret to an extern "ABI" fn(B, OtherArg) -> Ret is:

  • always valid;

  • safe / "correct" (no-UB) provided such a function is only called on instances of B which are safe to transmute to A (or just valid to transmute, if you happen to know that the body of the function does not rely on safety invariants…).

  • sound (to expose to/through an API) provided any instance of type B, is safe /sound to transmute to A;

To better see the corner cases, consider:

extern "ABI" fn foo (n: NonZeroU8) { … }

and then doing

let f =
        extern "ABI" fn(NonZeroU8),
        extern "ABI" fn(u8),  // `NonZeroU8` is a `#[repr(transparent)]` wrapper around `u8`

That is valid, but calling f(0) will cause UB since it would break the validity invariant of NonZeroU8 inside foo's body. So you cannot expose such a transmuted function pointer to/through a "public" API (unless you mark the resulting function pointer as unsafe), but you can safely use it yourself provided you make sure not to call it on 0.


This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.