Error handling

Rust has two parallel runtime error handling mechanisms: panics, and Result / ?.

Do not use panics for anything except unrecoverable discovery of a programming error (eg, assertion failure).

Result / ?

Rust has exceptionally good in-language support for functions which either return successfully, or return an error (such as an error code). This is the usual error handling style in Rust programs.

The core is the Result type in the standard library:

    pub enum Result<T,E> { Ok(T), Err(E) }

and a postfix operator ?. ? applied to an Ok simply unwraps the inner success value T. ? applied to an Err causes the containing function to return Err(E) after converting the error E to the containing function's error return type (using From).

An unfortunate downside is that all the returns from a fallible function must be written Ok(r) (or return Ok(r)). One must write Ok(()) at the end of a function which would otherwise fall off the end implicitly returning (). The fehler macro library addresses this problem; due to language limitations it is not perfect, but even so it greatly improves the ergonomics. (For some reason has failed to render fehler's

The compiler will tell you if you forget to write a needed ?. (If you tried to use the return value for something, it would have the wrong type; in case you don't, Result is marked #[must_use], generating a warning.)

(? can also be used with Option.)

In quick-and-dirty programs it is common to call unwrap (or expect), on a Result; these panic on errors. But, the return type from main can be a suitable Result. This, plus use of ? and a portmanteau error type like anyhow::Error, is usually better even in a prototype because it avoids writing unwrap calls that should be removed later to make the code production-ready.

Error types

The error type in a Result is generic.

The available and useful range of error types is too extensive to discuss here. But, consider:

  • anyhow (or eyre) for a boxed portmanteau error type; good for application programs which need to aggregate many kinds of error.

  • thiserror for defining your own error enum; good when you're writing a library.

  • Defining your own unit struct as the error type for a specific function or scenario. (Perhaps several such.)

  • std::io::Error if you primarily deal with OS errors.

In a sophisticated program errors often start out near the bottom of this list, and are progressively wrapped/converted into types nearer the top of the list.

Crate- and module-local Result and Error

Some modules (including, for example, std::io) define their own type called Error and their own Result to go with it.

This can be confusing. You can tell such a Result from std::result::Result (which is in the language prelude) because it will only have one type parameter: the success value: e.g. Result<()> instead of Result<(), io::Error>.

Exercise discretion before importing an unqualified Result that isn't std::result::Result, without renaming it. Consider whether maybe fehler's default #[throws] (meaning #[throws(Error)]) would be a better answer.


A panic is a synchronous unrecoverable failure of program execution, similar in some respects to a C++ exception.

Panics can be caused explicitly by panic!(), assert!, etc. The language will sometimes generate panics itself: for example, on arithmetic overflow in debug builds, or array bounds violation. There are no null pointer exceptions because references are never null - an optional reference is Option<&T>.

Libraries will sometimes generate panics, in cases of serious trouble. This should be documented, usually in an explicit Panics heading.

Typically panics produce a program crash with optional stack trace. Depending on the compilation settings, panics can perhaps be caught and recovered from, which involves unwinding including destroying the local variables in the unwound stack frames.

It is highly unidiomatic and inadvisable to use panics for handling of expected exceptional cases (eg, file not found). The very highest quality libraries offer completely panic-free versions of their functionality.