So in The Reference, I can't really find information regarding whether [...] fn([...]) [-> ...] has to reference a function callable with the given signature and ABI. We know behavior allowed by safe rust will try to be safe in the future, so since safe function pointers (e.g. fn(T) -> U, extern "C" fn(T) -> U) are callable without unsafe blocks, they must have an invariant like str, Pin<T> or &T that they properly reference a function with correct signature. But the same line of reasoning cannot be applied to unsafe function pointers (e.g. unsafe fn(T) -> U, unsafe extern "C" fn(T) -> U) because they need to be called within unsafe blocks.
So are unsafe [...] fn([...]) [-> ...]s more like NonNull<T> or &T?
For now I am using a #[repr(C)] union to represent "probably dangling" function pointers
In case you're not familiar with the distinction, some library/interface/whatever being "sound" means that safe code can not cause UB by using it. Your "callable without unsafe blocks" reasoning is about soundness, or what is sometimes called a safety invariant.[1] Safety invariants can be temporarily violated by unsafe code, but safe code should not be exposed to it -- that would be unsound. There are also validity invariants, which are always UB when violated.
The safety invariants of fn() and unsafe fn() are different, since you can't call the latter in safe code. So technically it's always sound to expose an unsafe fn() safe code.[2] But realistically, if you do expose one, you would have documentation detailing when it was valid to call the function or not.
The answer to "so when can I soundly call it" depends on how you got the value.
All that being said, why not just use Option<fn(..)>? Sentinel values beyond "don't call me"?
Some of your analogies are safety invariants, but others are validity invariants. ↩︎
that is NonNull and initialized -- otherwise you've already encountered UB before exposing anything ↩︎
Thank you for answering my question (transmute::<_, fn()>(1usize) is not immediate UB if it compiles, since transmute does width checking)! So it is unsound to expose an unsound-on-call safe fn() but the language does not make it immediate UB.
to answer your question, Option<fn(..)> seems to say "don't call me if I'm None", but FnPtr in
seems to say "don't call me if I'm any of those sentinel variants". Exposing the constants as Option<fn(..)> would make my library unsound, and I wasn't sure if B as Option<fn(..)> would be immediate UB
But given this information I can indeed just write FnPtr as