rust-guide icon indicating copy to clipboard operation
rust-guide copied to clipboard

Strictness of panicking

Open chrysn opened this issue 5 years ago • 3 comments

The way panics are discouraged ("Crates providing libraries should never use functions or instructions that can fail and cause the code to panic [including] assert, an unchecked access to an array") does not align with what I perceive to be the current best practice of general Rust development.

Oftentimes, panics resulting from array index access, expect calls or assertions are ruled out by the construction of a particular crate, and the invariants it upholds about its data structures. In a sense, for the "unchecked access to an array" item, the access is checked, but not at the very line before the access but somewhere further away (say, in the constructors). The compiler may or may not be able to follow that reasoning (and thus not even generate code for the panics) depending on the complexity of the invariant and the level of optimization. (Some invariants can even be made explicit in the type system, like for dividing by a NonZeroU32; others like "this index is suitable for that slice" can't yet).

The guidance as written brings the danger of functions that are conceptually infallible returning a Result for fear of violating LANG-NOPANIC. This imposes the onus of handling the error properly on the library users, who won't be able to make a good judgement on how to handle it because they know that it can't occur, and let them wind up with untestable code parts.

Suggested phrasing to avoid this:

Crates providing libraries should never use functions or instructions that can fail and cause the code to panic, unless they can ensure by invariants they uphold that the panic does not trigger. These invariants must be documented in the data structures, and referred to at the possibly panicking code.

Quite possibly it would be beneficial to even explicitly recommend against returning Results when an operation is actually infallible (not because it makes the own crate insecure, but makes securely using it harder), but that's another issue.

chrysn avatar Mar 21 '20 09:03 chrysn

I think you are absolutely right that pushing everything to the library user is not a general solution.

Crates providing libraries should never use functions or instructions can fail and cause to panic unless they can ensure by invariants they uphold that the panic does not trigger.

Nitpick: there is a contradiction here: if such invariants are verified, then such functions/instructions can't panic.

I think we need to clarify that the goal isn't to eliminate all theoretically panicky paths in library code, especially those the compiler can't prove unreachable due to its limitations. Instead, the focus should be on avoiding actual, triggerable panics in library code.

Regarding the issue of pushing unintelligible invariants onto the end-user, it highlights the general difficulty of writing clear errors for library user.


To go further, there are some alternate viewpoints:

  • The Rust API Guidelines and Clippy encourage documenting panic conditions within a library (e.g., via a dedicated # Panics section).
  • There may be scenarios where panics are unacceptable. For instance, in an embedded context without unwinding, panics are fatal. In such cases, the library user may prefer consistently fallible APIs rather than unexpected panics.
  • The std library increasingly offers fallible API replacements for previously panicking ones (for example, split_at_checked).

polazarus avatar Jun 23 '25 09:06 polazarus

Well the functions can, they just can't under the precise circumstances they're used in. Anyway, a more robust phrasing would be:

Crates providing libraries should never use functions or instructions that, under other circumstances, might fail and cause the code to panic, unless they can ensure by invariants they uphold that the panic does not trigger. These invariants must be documented in the data structures, and referred to at the possibly panicking code.


For instance, in an embedded context without unwinding, panics are fatal

Not immediately; in one embedded OS I work with, panics "just" kill the thread irrecoverably. Other threads can still run, but as the memory of the dead thread is gone, it's game-over soon.

chrysn avatar Jun 23 '25 14:06 chrysn

Not immediately; in one embedded OS I work with, panics "just" kill the thread irrecoverably. Other threads can still run, but as the memory of the dead thread is gone, it's game-over soon.

Does that deallocate the stack of said thread? If so that would be in conflict with the Pin drop guarantee. If not, that would just be equivalent to the thread never getting scheduled again, which is perfectly safe.

bjorn3 avatar Jun 23 '25 14:06 bjorn3