@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.