Skip to content

Commit 82beeab

Browse files
authored
Rollup merge of rust-lang#96033 - yaahc:expect-elaboration, r=scottmcm
Add section on common message styles for Result::expect Based on a question from rust-lang/project-error-handling#50 (comment) ~~One thing I haven't decided on yet, should I duplicate this section on `Option::expect`, link to this section, or move it somewhere else and link to that location from both docs?~~: I ended up moving the section to `std::error` and referencing it from both `Result::expect` and `Option::expect`'s docs. I think this section, when combined with the similar update I made on [`std::panic!`](https://doc.rust-lang.org/nightly/std/macro.panic.html#when-to-use-panic-vs-result) implies that we should possibly more aggressively encourage and support the "expect as precondition" style described in this section. The consensus among the libs team seems to be that panic should be used for bugs, not expected potential failure modes. The "expect as error message" style seems to align better with the panic for unrecoverable errors style where they're seen as normal errors where the only difference is a desire to kill the current execution unit (aka erlang style error handling). I'm wondering if we should be providing a panic hook similar to `human-panic` or more strongly recommending the "expect as precondition" style of expect message.
2 parents 1851f08 + ef879c6 commit 82beeab

File tree

3 files changed

+177
-1
lines changed

3 files changed

+177
-1
lines changed

library/core/src/option.rs

+20
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,26 @@ impl<T> Option<T> {
708708
/// let x: Option<&str> = None;
709709
/// x.expect("fruits are healthy"); // panics with `fruits are healthy`
710710
/// ```
711+
///
712+
/// # Recommended Message Style
713+
///
714+
/// We recommend that `expect` messages are used to describe the reason you
715+
/// _expect_ the `Option` should be `Some`.
716+
///
717+
/// ```should_panic
718+
/// # let slice: &[u8] = &[];
719+
/// let item = slice.get(0)
720+
/// .expect("slice should not be empty");
721+
/// ```
722+
///
723+
/// **Hint**: If you're having trouble remembering how to phrase expect
724+
/// error messages remember to focus on the word "should" as in "env
725+
/// variable should be set by blah" or "the given binary should be available
726+
/// and executable by the current user".
727+
///
728+
/// For more detail on expect message styles and the reasoning behind our
729+
/// recommendation please refer to the section on ["Common Message
730+
/// Styles"](../../std/error/index.html#common-message-styles) in the [`std::error`](../../std/error/index.html) module docs.
711731
#[inline]
712732
#[track_caller]
713733
#[stable(feature = "rust1", since = "1.0.0")]

library/core/src/result.rs

+20
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,26 @@ impl<T, E> Result<T, E> {
10231023
/// let x: Result<u32, &str> = Err("emergency failure");
10241024
/// x.expect("Testing expect"); // panics with `Testing expect: emergency failure`
10251025
/// ```
1026+
///
1027+
/// # Recommended Message Style
1028+
///
1029+
/// We recommend that `expect` messages are used to describe the reason you
1030+
/// _expect_ the `Result` should be `Ok`.
1031+
///
1032+
/// ```should_panic
1033+
/// let path = std::env::var("IMPORTANT_PATH")
1034+
/// .expect("env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`");
1035+
/// ```
1036+
///
1037+
/// **Hint**: If you're having trouble remembering how to phrase expect
1038+
/// error messages remember to focus on the word "should" as in "env
1039+
/// variable should be set by blah" or "the given binary should be available
1040+
/// and executable by the current user".
1041+
///
1042+
/// For more detail on expect message styles and the reasoning behind our recommendation please
1043+
/// refer to the section on ["Common Message
1044+
/// Styles"](../../std/error/index.html#common-message-styles) in the
1045+
/// [`std::error`](../../std/error/index.html) module docs.
10261046
#[inline]
10271047
#[track_caller]
10281048
#[stable(feature = "result_expect", since = "1.4.0")]

library/std/src/error.rs

+137-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,140 @@
1-
//! Traits for working with Errors.
1+
//! Interfaces for working with Errors.
2+
//!
3+
//! # Error Handling In Rust
4+
//!
5+
//! The Rust language provides two complementary systems for constructing /
6+
//! representing, reporting, propagating, reacting to, and discarding errors.
7+
//! These responsibilities are collectively known as "error handling." The
8+
//! components of the first system, the panic runtime and interfaces, are most
9+
//! commonly used to represent bugs that have been detected in your program. The
10+
//! components of the second system, `Result`, the error traits, and user
11+
//! defined types, are used to represent anticipated runtime failure modes of
12+
//! your program.
13+
//!
14+
//! ## The Panic Interfaces
15+
//!
16+
//! The following are the primary interfaces of the panic system and the
17+
//! responsibilities they cover:
18+
//!
19+
//! * [`panic!`] and [`panic_any`] (Constructing, Propagated automatically)
20+
//! * [`PanicInfo`] (Reporting)
21+
//! * [`set_hook`], [`take_hook`], and [`#[panic_handler]`][panic-handler] (Reporting)
22+
//! * [`catch_unwind`] and [`resume_unwind`] (Discarding, Propagating)
23+
//!
24+
//! The following are the primary interfaces of the error system and the
25+
//! responsibilities they cover:
26+
//!
27+
//! * [`Result`] (Propagating, Reacting)
28+
//! * The [`Error`] trait (Reporting)
29+
//! * User defined types (Constructing / Representing)
30+
//! * [`match`] and [`downcast`] (Reacting)
31+
//! * The question mark operator ([`?`]) (Propagating)
32+
//! * The partially stable [`Try`] traits (Propagating, Constructing)
33+
//! * [`Termination`] (Reporting)
34+
//!
35+
//! ## Converting Errors into Panics
36+
//!
37+
//! The panic and error systems are not entirely distinct. Often times errors
38+
//! that are anticipated runtime failures in an API might instead represent bugs
39+
//! to a caller. For these situations the standard library provides APIs for
40+
//! constructing panics with an `Error` as it's source.
41+
//!
42+
//! * [`Result::unwrap`]
43+
//! * [`Result::expect`]
44+
//!
45+
//! These functions are equivalent, they either return the inner value if the
46+
//! `Result` is `Ok` or panic if the `Result` is `Err` printing the inner error
47+
//! as the source. The only difference between them is that with `expect` you
48+
//! provide a panic error message to be printed alongside the source, whereas
49+
//! `unwrap` has a default message indicating only that you unwraped an `Err`.
50+
//!
51+
//! Of the two, `expect` is generally preferred since its `msg` field allows you
52+
//! to convey your intent and assumptions which makes tracking down the source
53+
//! of a panic easier. `unwrap` on the other hand can still be a good fit in
54+
//! situations where you can trivially show that a piece of code will never
55+
//! panick, such as `"127.0.0.1".parse::<std::net::IpAddr>().unwrap()` or early
56+
//! prototyping.
57+
//!
58+
//! # Common Message Styles
59+
//!
60+
//! There are two common styles for how people word `expect` messages. Using
61+
//! the message to present information to users encountering a panic
62+
//! ("expect as error message") or using the message to present information
63+
//! to developers debugging the panic ("expect as precondition").
64+
//!
65+
//! In the former case the expect message is used to describe the error that
66+
//! has occurred which is considered a bug. Consider the following example:
67+
//!
68+
//! ```should_panic
69+
//! // Read environment variable, panic if it is not present
70+
//! let path = std::env::var("IMPORTANT_PATH").unwrap();
71+
//! ```
72+
//!
73+
//! In the "expect as error message" style we would use expect to describe
74+
//! that the environment variable was not set when it should have been:
75+
//!
76+
//! ```should_panic
77+
//! let path = std::env::var("IMPORTANT_PATH")
78+
//! .expect("env variable `IMPORTANT_PATH` is not set");
79+
//! ```
80+
//!
81+
//! In the "expect as precondition" style, we would instead describe the
82+
//! reason we _expect_ the `Result` should be `Ok`. With this style we would
83+
//! prefer to write:
84+
//!
85+
//! ```should_panic
86+
//! let path = std::env::var("IMPORTANT_PATH")
87+
//! .expect("env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`");
88+
//! ```
89+
//!
90+
//! The "expect as error message" style does not work as well with the
91+
//! default output of the std panic hooks, and often ends up repeating
92+
//! information that is already communicated by the source error being
93+
//! unwrapped:
94+
//!
95+
//! ```text
96+
//! thread 'main' panicked at 'env variable `IMPORTANT_PATH` is not set: NotPresent', src/main.rs:4:6
97+
//! ```
98+
//!
99+
//! In this example we end up mentioning that an env variable is not set,
100+
//! followed by our source message that says the env is not present, the
101+
//! only additional information we're communicating is the name of the
102+
//! environment variable being checked.
103+
//!
104+
//! The "expect as precondition" style instead focuses on source code
105+
//! readability, making it easier to understand what must have gone wrong in
106+
//! situations where panics are being used to represent bugs exclusively.
107+
//! Also, by framing our expect in terms of what "SHOULD" have happened to
108+
//! prevent the source error, we end up introducing new information that is
109+
//! independent from our source error.
110+
//!
111+
//! ```text
112+
//! thread 'main' panicked at 'env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`: NotPresent', src/main.rs:4:6
113+
//! ```
114+
//!
115+
//! In this example we are communicating not only the name of the
116+
//! environment variable that should have been set, but also an explanation
117+
//! for why it should have been set, and we let the source error display as
118+
//! a clear contradiction to our expectation.
119+
//!
120+
//! **Hint**: If you're having trouble remembering how to phrase
121+
//! expect-as-precondition style error messages remember to focus on the word
122+
//! "should" as in "env variable should be set by blah" or "the given binary
123+
//! should be available and executable by the current user".
124+
//!
125+
//! [`panic_any`]: crate::panic::panic_any
126+
//! [`PanicInfo`]: crate::panic::PanicInfo
127+
//! [`catch_unwind`]: crate::panic::catch_unwind
128+
//! [`resume_unwind`]: crate::panic::resume_unwind
129+
//! [`downcast`]: crate::error::Error
130+
//! [`Termination`]: crate::process::Termination
131+
//! [`Try`]: crate::ops::Try
132+
//! [panic hook]: crate::panic::set_hook
133+
//! [`set_hook`]: crate::panic::set_hook
134+
//! [`take_hook`]: crate::panic::take_hook
135+
//! [panic-handler]: <https://doc.rust-lang.org/nomicon/panic-handler.html>
136+
//! [`match`]: ../../std/keyword.match.html
137+
//! [`?`]: ../../std/result/index.html#the-question-mark-operator-
2138
3139
#![stable(feature = "rust1", since = "1.0.0")]
4140

0 commit comments

Comments
 (0)