@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 =
transmute::<
extern "ABI" fn(NonZeroU8),
extern "ABI" fn(u8), // `NonZeroU8` is a `#[repr(transparent)]` wrapper around `u8`
>(foo)
;
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
.