Why
In today's Rust, the concept of panic, a critical runtime error, exists outside of the type system. While this allows us to code without thinking much about it, people who code in embedded, bare-metal, or do FFI, really do have to. There have been multiple efforts to tackle this outside of the language, like extra tooling or linker scripts, but these are workarounds and cumbersome at best.
How
We currently have different traits for functions: Fn
, FnOnce
, and FnMut
. On closures, these are implemented based on what the closure does with its captured variables. My idea is to have another Fn
trait for those that do not panic, e.g. FnNoPanic
, which is implemented by all functions that may never (safely) trigger stack unwinding. Then, we would be able to do let x: impl FnNoPanic = || foo();
in order to ensure panic safety. This way, if foo()
called code that, in turn, could panic, it would result in a compilation error.
Attribute
In order to have these checks performed at the function declaration boundary, the most straightforward way would be an attribute. We could have a #[no_panic]
attribute that ensures that the function implements the FnNoPanic
trait. With this header, the function will not compile if it may panic.
Semver
However, this is a HUGE semver hazard. Changing the internal code of a function (not the interface) would have an effect on the callers! For this reason, I propose this to not be part of semver unless the attribute is used. I fear this is not enough and would create lots of footguns, so a reasonable solution would be to have the FnNoPanic
trait be only implemented if the attribute is present or all called functions have this attribute; and then have a way to unsafely implement it if a function from another crate doesn't implement it, but should.
Function pointers
Functions pointers do and will not make any guarantees about panic safety, so any function pointer that can't be tracked while type-checking (function pointers as function arguments, for example), are assumed not to implement FnNoPanic
.
External calls
Any external function (rust ABI or not) must implement the FnNoPanic
trait unsafely. This is because the compiler is unable to check them directly.
Unsafe implementation of the attribute
Unsafe attributes are a feature that has been discussed for quite long, and we could use them to implement this unsafely. Bikeshed: #[unsafe(no_panic)]
.