Rust Model of Error Handling

tags: Error Handling

Definitions

This section is for widely used terms in discussions around error handling. The goal of this section is to reduce confusion where different people interpret the same terms in different ways when talking about error handling.

  • Error: an unexpected or exceptional result of some computation. In Rust, these are often represented as an instance of Result::Err. Can also refer to the object representing the error which is usally wrapped in Result::Err and whose type may implement the Error trait.
  • Exception: a language mechanism where errors can be passed over multiple functions in the call stack to be handled by an indirect caller. Rust does not have exceptions, but if you squint, panicking and catching panics is exception-like.
  • Recoverable and unrecoverable errors: an error is recoverable if the thread can continue executing after handling the error. If the thread terminates, the error is uncoverable. Whether an error is recoverable might be a property of the kind of error, how the error is propogated, or how the error is handled.
  • Panic:
  • Error handling:
  • Error recovery:
  • Error reporting:
  • Backtrace (aka stack trace):
    • Panic backtrace:
    • Error backtrace:
    • Backtrace in debuggers:
  • Downcasting: dynamically changing the type of a trait object to a more precise type (a subtype), that may be either a concrete type or a trait object with more precise bounds. In Rust there is no syntax for downcasting, and it is usually accomplished using a method on the trait object.
  • The Panic Runtime
    • #[panic_handler] language item
    • unwinding/aborting panics
    • std panic hook
    • panic macros
    • PanicInfo
    • catch_unwind/resume_unwind
    • panic_any
  • Fallibility Propagation
    • Result
    • the ? operator
    • The Try trait and associated traits (FromResidual, Residual, etc)
    • Try blocks
  • The Error trait
    • Display supertrait for error messages
    • source method for composing errors and iterating over sources
    • downcast for reacting to specific errors after they've been type erased

Core Design Goals

This section is for the core design goals for error handling in rust. These goals should represent an ideal world, not necessarily what we currently have.

  • Recoverable error propagation should be explicit/visible
  • Unrecoverable error propagation should be hidden / should not affect public APIs
  • It shouldn't be possible to accidentally discard an error and continue as if one had not occured
  • Error reports should be consistent and cohesive
  • It should be possible for the same error to be formatted differently depending on where it is being displayed (e.g. multiple lines if going to a terminal but all in a single line if going to a log file)
  • Errors from different libraries should compose nicely
  • It should be easy to define new errors
  • It should be easy to react to specific recoverable errors at runtime
  • Introducing new errors or changing error messages shouldn't be a breaking change by default
  • Errors should be fast by default
    • Do not assume the error path is uncommon in general (though it often is, this should be decided case-by-case)
  • Errors should be informative by default
  • It should be easy to promote a recoverable error to a non-recoverable one
  • Reporting errors should be consistent regardless of mechanism of propagation (panics vs Try)
  • Information should not be lost when reporting errors
  • Distinguish error reporting for logging (and similar activities such as debugging), and for user-facing error messages.
    • Logging should maximise information content, be consistent and structured, and include implementation details. May be integrated with tooling.
    • User reporting should be tailored to the user, localisable, and rendered using the UI of the application.
    • In general, only applications should do user-facing error reporting. Libraries may do some logging of errors.
    • Handling an error might involve zero, one, or both kinds of error reporting.

Current Areas of Confusion

  • The relationship between panics (unrecoverable error handling) and the error trait / result (recoverable error handling)
  • Recommended structure / style for error messages
  • Error::source usage / best practices
  • If an error type should/must implement the Error trait.
  • The error library ecosystem: should an application/library use one or several? How they interact?
  • When to use opaque (trait object) errors vs concrete types (for nested errors as well as at the top level).
Select a repo