Skip to content

Commit

Permalink
unify error module docs
Browse files Browse the repository at this point in the history
  • Loading branch information
yaahc committed Aug 17, 2022
1 parent ada65c2 commit 7bcfe47
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 140 deletions.
137 changes: 137 additions & 0 deletions library/core/src/error.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
Interfaces for working with Errors.

# Error Handling In Rust

The Rust language provides two complementary systems for constructing /
representing, reporting, propagating, reacting to, and discarding errors.
These responsibilities are collectively known as "error handling." The
components of the first system, the panic runtime and interfaces, are most
commonly used to represent bugs that have been detected in your program. The
components of the second system, `Result`, the error traits, and user
defined types, are used to represent anticipated runtime failure modes of
your program.

## The Panic Interfaces

The following are the primary interfaces of the panic system and the
responsibilities they cover:

* [`panic!`] and [`panic_any`] (Constructing, Propagated automatically)
* [`PanicInfo`] (Reporting)
* [`set_hook`], [`take_hook`], and [`#[panic_handler]`][panic-handler] (Reporting)
* [`catch_unwind`] and [`resume_unwind`] (Discarding, Propagating)

The following are the primary interfaces of the error system and the
responsibilities they cover:

* [`Result`] (Propagating, Reacting)
* The [`Error`] trait (Reporting)
* User defined types (Constructing / Representing)
* [`match`] and [`downcast`] (Reacting)
* The question mark operator ([`?`]) (Propagating)
* The partially stable [`Try`] traits (Propagating, Constructing)
* [`Termination`] (Reporting)

## Converting Errors into Panics

The panic and error systems are not entirely distinct. Often times errors
that are anticipated runtime failures in an API might instead represent bugs
to a caller. For these situations the standard library provides APIs for
constructing panics with an `Error` as it's source.

* [`Result::unwrap`]
* [`Result::expect`]

These functions are equivalent, they either return the inner value if the
`Result` is `Ok` or panic if the `Result` is `Err` printing the inner error
as the source. The only difference between them is that with `expect` you
provide a panic error message to be printed alongside the source, whereas
`unwrap` has a default message indicating only that you unwraped an `Err`.

Of the two, `expect` is generally preferred since its `msg` field allows you
to convey your intent and assumptions which makes tracking down the source
of a panic easier. `unwrap` on the other hand can still be a good fit in
situations where you can trivially show that a piece of code will never
panic, such as `"127.0.0.1".parse::<std::net::IpAddr>().unwrap()` or early
prototyping.

# Common Message Styles

There are two common styles for how people word `expect` messages. Using
the message to present information to users encountering a panic
("expect as error message") or using the message to present information
to developers debugging the panic ("expect as precondition").

In the former case the expect message is used to describe the error that
has occurred which is considered a bug. Consider the following example:

```should_panic
// Read environment variable, panic if it is not present
let path = std::env::var("IMPORTANT_PATH").unwrap();
```

In the "expect as error message" style we would use expect to describe
that the environment variable was not set when it should have been:

```should_panic
let path = std::env::var("IMPORTANT_PATH")
.expect("env variable `IMPORTANT_PATH` is not set");
```

In the "expect as precondition" style, we would instead describe the
reason we _expect_ the `Result` should be `Ok`. With this style we would
prefer to write:

```should_panic
let path = std::env::var("IMPORTANT_PATH")
.expect("env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`");
```

The "expect as error message" style does not work as well with the
default output of the std panic hooks, and often ends up repeating
information that is already communicated by the source error being
unwrapped:

```text
thread 'main' panicked at 'env variable `IMPORTANT_PATH` is not set: NotPresent', src/main.rs:4:6
```

In this example we end up mentioning that an env variable is not set,
followed by our source message that says the env is not present, the
only additional information we're communicating is the name of the
environment variable being checked.

The "expect as precondition" style instead focuses on source code
readability, making it easier to understand what must have gone wrong in
situations where panics are being used to represent bugs exclusively.
Also, by framing our expect in terms of what "SHOULD" have happened to
prevent the source error, we end up introducing new information that is
independent from our source error.

```text
thread 'main' panicked at 'env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`: NotPresent', src/main.rs:4:6
```

In this example we are communicating not only the name of the
environment variable that should have been set, but also an explanation
for why it should have been set, and we let the source error display as
a clear contradiction to our expectation.

**Hint**: If you're having trouble remembering how to phrase
expect-as-precondition style error messages remember to focus on the word
"should" as in "env variable should be set by blah" or "the given binary
should be available and executable by the current user".

[`panic_any`]: crate::panic::panic_any
[`PanicInfo`]: crate::panic::PanicInfo
[`catch_unwind`]: crate::panic::catch_unwind
[`resume_unwind`]: crate::panic::resume_unwind
[`downcast`]: crate::error::Error
[`Termination`]: crate::process::Termination
[`Try`]: crate::ops::Try
[panic hook]: crate::panic::set_hook
[`set_hook`]: crate::panic::set_hook
[`take_hook`]: crate::panic::take_hook
[panic-handler]: <https://doc.rust-lang.org/nomicon/panic-handler.html>
[`match`]: ../../std/keyword.match.html
[`?`]: ../../std/result/index.html#the-question-mark-operator-
3 changes: 1 addition & 2 deletions library/core/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//! Interfaces for working with Errors.
// FIXME(yaahc): link to std docs or common docs and try to figure out intradoc issue
#![doc = include_str!("error.md")]
#![unstable(feature = "error_in_core", issue = "none")]

// A note about crates and the facade:
Expand Down
139 changes: 1 addition & 138 deletions library/std/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,141 +1,4 @@
//! Interfaces for working with Errors.
//!
//! # Error Handling In Rust
//!
//! The Rust language provides two complementary systems for constructing /
//! representing, reporting, propagating, reacting to, and discarding errors.
//! These responsibilities are collectively known as "error handling." The
//! components of the first system, the panic runtime and interfaces, are most
//! commonly used to represent bugs that have been detected in your program. The
//! components of the second system, `Result`, the error traits, and user
//! defined types, are used to represent anticipated runtime failure modes of
//! your program.
//!
//! ## The Panic Interfaces
//!
//! The following are the primary interfaces of the panic system and the
//! responsibilities they cover:
//!
//! * [`panic!`] and [`panic_any`] (Constructing, Propagated automatically)
//! * [`PanicInfo`] (Reporting)
//! * [`set_hook`], [`take_hook`], and [`#[panic_handler]`][panic-handler] (Reporting)
//! * [`catch_unwind`] and [`resume_unwind`] (Discarding, Propagating)
//!
//! The following are the primary interfaces of the error system and the
//! responsibilities they cover:
//!
//! * [`Result`] (Propagating, Reacting)
//! * The [`Error`] trait (Reporting)
//! * User defined types (Constructing / Representing)
//! * [`match`] and [`downcast`] (Reacting)
//! * The question mark operator ([`?`]) (Propagating)
//! * The partially stable [`Try`] traits (Propagating, Constructing)
//! * [`Termination`] (Reporting)
//!
//! ## Converting Errors into Panics
//!
//! The panic and error systems are not entirely distinct. Often times errors
//! that are anticipated runtime failures in an API might instead represent bugs
//! to a caller. For these situations the standard library provides APIs for
//! constructing panics with an `Error` as it's source.
//!
//! * [`Result::unwrap`]
//! * [`Result::expect`]
//!
//! These functions are equivalent, they either return the inner value if the
//! `Result` is `Ok` or panic if the `Result` is `Err` printing the inner error
//! as the source. The only difference between them is that with `expect` you
//! provide a panic error message to be printed alongside the source, whereas
//! `unwrap` has a default message indicating only that you unwraped an `Err`.
//!
//! Of the two, `expect` is generally preferred since its `msg` field allows you
//! to convey your intent and assumptions which makes tracking down the source
//! of a panic easier. `unwrap` on the other hand can still be a good fit in
//! situations where you can trivially show that a piece of code will never
//! panic, such as `"127.0.0.1".parse::<std::net::IpAddr>().unwrap()` or early
//! prototyping.
//!
//! # Common Message Styles
//!
//! There are two common styles for how people word `expect` messages. Using
//! the message to present information to users encountering a panic
//! ("expect as error message") or using the message to present information
//! to developers debugging the panic ("expect as precondition").
//!
//! In the former case the expect message is used to describe the error that
//! has occurred which is considered a bug. Consider the following example:
//!
//! ```should_panic
//! // Read environment variable, panic if it is not present
//! let path = std::env::var("IMPORTANT_PATH").unwrap();
//! ```
//!
//! In the "expect as error message" style we would use expect to describe
//! that the environment variable was not set when it should have been:
//!
//! ```should_panic
//! let path = std::env::var("IMPORTANT_PATH")
//! .expect("env variable `IMPORTANT_PATH` is not set");
//! ```
//!
//! In the "expect as precondition" style, we would instead describe the
//! reason we _expect_ the `Result` should be `Ok`. With this style we would
//! prefer to write:
//!
//! ```should_panic
//! let path = std::env::var("IMPORTANT_PATH")
//! .expect("env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`");
//! ```
//!
//! The "expect as error message" style does not work as well with the
//! default output of the std panic hooks, and often ends up repeating
//! information that is already communicated by the source error being
//! unwrapped:
//!
//! ```text
//! thread 'main' panicked at 'env variable `IMPORTANT_PATH` is not set: NotPresent', src/main.rs:4:6
//! ```
//!
//! In this example we end up mentioning that an env variable is not set,
//! followed by our source message that says the env is not present, the
//! only additional information we're communicating is the name of the
//! environment variable being checked.
//!
//! The "expect as precondition" style instead focuses on source code
//! readability, making it easier to understand what must have gone wrong in
//! situations where panics are being used to represent bugs exclusively.
//! Also, by framing our expect in terms of what "SHOULD" have happened to
//! prevent the source error, we end up introducing new information that is
//! independent from our source error.
//!
//! ```text
//! thread 'main' panicked at 'env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`: NotPresent', src/main.rs:4:6
//! ```
//!
//! In this example we are communicating not only the name of the
//! environment variable that should have been set, but also an explanation
//! for why it should have been set, and we let the source error display as
//! a clear contradiction to our expectation.
//!
//! **Hint**: If you're having trouble remembering how to phrase
//! expect-as-precondition style error messages remember to focus on the word
//! "should" as in "env variable should be set by blah" or "the given binary
//! should be available and executable by the current user".
//!
//! [`panic_any`]: crate::panic::panic_any
//! [`PanicInfo`]: crate::panic::PanicInfo
//! [`catch_unwind`]: crate::panic::catch_unwind
//! [`resume_unwind`]: crate::panic::resume_unwind
//! [`downcast`]: crate::error::Error
//! [`Termination`]: crate::process::Termination
//! [`Try`]: crate::ops::Try
//! [panic hook]: crate::panic::set_hook
//! [`set_hook`]: crate::panic::set_hook
//! [`take_hook`]: crate::panic::take_hook
//! [panic-handler]: <https://doc.rust-lang.org/nomicon/panic-handler.html>
//! [`match`]: ../../std/keyword.match.html
//! [`?`]: ../../std/result/index.html#the-question-mark-operator-

#![doc = include_str!("../../core/src/error.md")]
#![stable(feature = "rust1", since = "1.0.0")]

// A note about crates and the facade:
Expand Down

0 comments on commit 7bcfe47

Please sign in to comment.