Auto merge of #99917 - yaahc:error-in-core-move, r=thomcc

Move Error trait into core

This PR moves the error trait from the standard library into a new unstable `error` module within the core library. The goal of this PR is to help unify error reporting across the std and no_std ecosystems, as well as open the door to integrating the error trait into the panic reporting system when reporting panics whose source is an errors (such as via `expect`).

This PR is a rewrite of https://github.com/rust-lang/rust/pull/90328 using new compiler features that have been added to support error in core.
This commit is contained in:
bors 2022-08-23 19:48:55 +00:00
commit 060e47f74a
25 changed files with 1264 additions and 226 deletions

View File

@ -151,6 +151,8 @@ use core::async_iter::AsyncIterator;
use core::borrow;
use core::cmp::Ordering;
use core::convert::{From, TryFrom};
#[cfg(not(bootstrap))]
use core::error::Error;
use core::fmt;
use core::future::Future;
use core::hash::{Hash, Hasher};
@ -174,6 +176,9 @@ use crate::borrow::Cow;
use crate::raw_vec::RawVec;
#[cfg(not(no_global_oom_handling))]
use crate::str::from_boxed_utf8_unchecked;
#[cfg(not(bootstrap))]
#[cfg(not(no_global_oom_handling))]
use crate::string::String;
#[cfg(not(no_global_oom_handling))]
use crate::vec::Vec;
@ -2085,3 +2090,304 @@ impl<S: ?Sized + AsyncIterator + Unpin> AsyncIterator for Box<S> {
(**self).size_hint()
}
}
#[cfg(not(bootstrap))]
impl dyn Error {
#[inline]
#[stable(feature = "error_downcast", since = "1.3.0")]
#[rustc_allow_incoherent_impl]
/// Attempts to downcast the box to a concrete type.
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error>> {
if self.is::<T>() {
unsafe {
let raw: *mut dyn Error = Box::into_raw(self);
Ok(Box::from_raw(raw as *mut T))
}
} else {
Err(self)
}
}
}
#[cfg(not(bootstrap))]
impl dyn Error + Send {
#[inline]
#[stable(feature = "error_downcast", since = "1.3.0")]
#[rustc_allow_incoherent_impl]
/// Attempts to downcast the box to a concrete type.
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error + Send>> {
let err: Box<dyn Error> = self;
<dyn Error>::downcast(err).map_err(|s| unsafe {
// Reapply the `Send` marker.
mem::transmute::<Box<dyn Error>, Box<dyn Error + Send>>(s)
})
}
}
#[cfg(not(bootstrap))]
impl dyn Error + Send + Sync {
#[inline]
#[stable(feature = "error_downcast", since = "1.3.0")]
#[rustc_allow_incoherent_impl]
/// Attempts to downcast the box to a concrete type.
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
let err: Box<dyn Error> = self;
<dyn Error>::downcast(err).map_err(|s| unsafe {
// Reapply the `Send + Sync` marker.
mem::transmute::<Box<dyn Error>, Box<dyn Error + Send + Sync>>(s)
})
}
}
#[cfg(not(bootstrap))]
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, E: Error + 'a> From<E> for Box<dyn Error + 'a> {
/// Converts a type of [`Error`] into a box of dyn [`Error`].
///
/// # Examples
///
/// ```
/// use std::error::Error;
/// use std::fmt;
/// use std::mem;
///
/// #[derive(Debug)]
/// struct AnError;
///
/// impl fmt::Display for AnError {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// write!(f, "An error")
/// }
/// }
///
/// impl Error for AnError {}
///
/// let an_error = AnError;
/// assert!(0 == mem::size_of_val(&an_error));
/// let a_boxed_error = Box::<dyn Error>::from(an_error);
/// assert!(mem::size_of::<Box<dyn Error>>() == mem::size_of_val(&a_boxed_error))
/// ```
fn from(err: E) -> Box<dyn Error + 'a> {
Box::new(err)
}
}
#[cfg(not(bootstrap))]
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, E: Error + Send + Sync + 'a> From<E> for Box<dyn Error + Send + Sync + 'a> {
/// Converts a type of [`Error`] + [`Send`] + [`Sync`] into a box of
/// dyn [`Error`] + [`Send`] + [`Sync`].
///
/// # Examples
///
/// ```
/// use std::error::Error;
/// use std::fmt;
/// use std::mem;
///
/// #[derive(Debug)]
/// struct AnError;
///
/// impl fmt::Display for AnError {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// write!(f, "An error")
/// }
/// }
///
/// impl Error for AnError {}
///
/// unsafe impl Send for AnError {}
///
/// unsafe impl Sync for AnError {}
///
/// let an_error = AnError;
/// assert!(0 == mem::size_of_val(&an_error));
/// let a_boxed_error = Box::<dyn Error + Send + Sync>::from(an_error);
/// assert!(
/// mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error))
/// ```
fn from(err: E) -> Box<dyn Error + Send + Sync + 'a> {
Box::new(err)
}
}
#[cfg(not(bootstrap))]
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
impl From<String> for Box<dyn Error + Send + Sync> {
/// Converts a [`String`] into a box of dyn [`Error`] + [`Send`] + [`Sync`].
///
/// # Examples
///
/// ```
/// use std::error::Error;
/// use std::mem;
///
/// let a_string_error = "a string error".to_string();
/// let a_boxed_error = Box::<dyn Error + Send + Sync>::from(a_string_error);
/// assert!(
/// mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error))
/// ```
#[inline]
fn from(err: String) -> Box<dyn Error + Send + Sync> {
struct StringError(String);
impl Error for StringError {
#[allow(deprecated)]
fn description(&self) -> &str {
&self.0
}
}
impl fmt::Display for StringError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
// Purposefully skip printing "StringError(..)"
impl fmt::Debug for StringError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
Box::new(StringError(err))
}
}
#[cfg(not(bootstrap))]
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "string_box_error", since = "1.6.0")]
impl From<String> for Box<dyn Error> {
/// Converts a [`String`] into a box of dyn [`Error`].
///
/// # Examples
///
/// ```
/// use std::error::Error;
/// use std::mem;
///
/// let a_string_error = "a string error".to_string();
/// let a_boxed_error = Box::<dyn Error>::from(a_string_error);
/// assert!(mem::size_of::<Box<dyn Error>>() == mem::size_of_val(&a_boxed_error))
/// ```
fn from(str_err: String) -> Box<dyn Error> {
let err1: Box<dyn Error + Send + Sync> = From::from(str_err);
let err2: Box<dyn Error> = err1;
err2
}
}
#[cfg(not(bootstrap))]
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> From<&str> for Box<dyn Error + Send + Sync + 'a> {
/// Converts a [`str`] into a box of dyn [`Error`] + [`Send`] + [`Sync`].
///
/// [`str`]: prim@str
///
/// # Examples
///
/// ```
/// use std::error::Error;
/// use std::mem;
///
/// let a_str_error = "a str error";
/// let a_boxed_error = Box::<dyn Error + Send + Sync>::from(a_str_error);
/// assert!(
/// mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error))
/// ```
#[inline]
fn from(err: &str) -> Box<dyn Error + Send + Sync + 'a> {
From::from(String::from(err))
}
}
#[cfg(not(bootstrap))]
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "string_box_error", since = "1.6.0")]
impl From<&str> for Box<dyn Error> {
/// Converts a [`str`] into a box of dyn [`Error`].
///
/// [`str`]: prim@str
///
/// # Examples
///
/// ```
/// use std::error::Error;
/// use std::mem;
///
/// let a_str_error = "a str error";
/// let a_boxed_error = Box::<dyn Error>::from(a_str_error);
/// assert!(mem::size_of::<Box<dyn Error>>() == mem::size_of_val(&a_boxed_error))
/// ```
fn from(err: &str) -> Box<dyn Error> {
From::from(String::from(err))
}
}
#[cfg(not(bootstrap))]
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "cow_box_error", since = "1.22.0")]
impl<'a, 'b> From<Cow<'b, str>> for Box<dyn Error + Send + Sync + 'a> {
/// Converts a [`Cow`] into a box of dyn [`Error`] + [`Send`] + [`Sync`].
///
/// # Examples
///
/// ```
/// use std::error::Error;
/// use std::mem;
/// use std::borrow::Cow;
///
/// let a_cow_str_error = Cow::from("a str error");
/// let a_boxed_error = Box::<dyn Error + Send + Sync>::from(a_cow_str_error);
/// assert!(
/// mem::size_of::<Box<dyn Error + Send + Sync>>() == mem::size_of_val(&a_boxed_error))
/// ```
fn from(err: Cow<'b, str>) -> Box<dyn Error + Send + Sync + 'a> {
From::from(String::from(err))
}
}
#[cfg(not(bootstrap))]
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "cow_box_error", since = "1.22.0")]
impl<'a> From<Cow<'a, str>> for Box<dyn Error> {
/// Converts a [`Cow`] into a box of dyn [`Error`].
///
/// # Examples
///
/// ```
/// use std::error::Error;
/// use std::mem;
/// use std::borrow::Cow;
///
/// let a_cow_str_error = Cow::from("a str error");
/// let a_boxed_error = Box::<dyn Error>::from(a_cow_str_error);
/// assert!(mem::size_of::<Box<dyn Error>>() == mem::size_of_val(&a_boxed_error))
/// ```
fn from(err: Cow<'a, str>) -> Box<dyn Error> {
From::from(String::from(err))
}
}
#[cfg(not(bootstrap))]
#[stable(feature = "box_error", since = "1.8.0")]
impl<T: core::error::Error> core::error::Error for Box<T> {
#[allow(deprecated, deprecated_in_future)]
fn description(&self) -> &str {
core::error::Error::description(&**self)
}
#[allow(deprecated)]
fn cause(&self) -> Option<&dyn core::error::Error> {
core::error::Error::cause(&**self)
}
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
core::error::Error::source(&**self)
}
}

View File

@ -2,6 +2,8 @@
// https://github.com/matthieu-m/rfc2580/blob/b58d1d3cba0d4b5e859d3617ea2d0943aaa31329/examples/thin.rs
// by matthieu-m
use crate::alloc::{self, Layout, LayoutError};
#[cfg(not(bootstrap))]
use core::error::Error;
use core::fmt::{self, Debug, Display, Formatter};
use core::marker::PhantomData;
#[cfg(not(no_global_oom_handling))]
@ -271,3 +273,11 @@ impl<H> WithHeader<H> {
Layout::new::<H>().extend(value_layout)
}
}
#[cfg(not(bootstrap))]
#[unstable(feature = "thin_box", issue = "92791")]
impl<T: ?Sized + Error> Error for ThinBox<T> {
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.deref().source()
}
}

View File

@ -133,6 +133,17 @@ impl<'a, K: Debug + Ord, V: Debug, A: Allocator + Clone> fmt::Display
}
}
#[cfg(not(bootstrap))]
#[unstable(feature = "map_try_insert", issue = "82766")]
impl<'a, K: core::fmt::Debug + Ord, V: core::fmt::Debug> core::error::Error
for crate::collections::btree_map::OccupiedError<'a, K, V>
{
#[allow(deprecated)]
fn description(&self) -> &str {
"key already exists"
}
}
impl<'a, K: Ord, V, A: Allocator + Clone> Entry<'a, K, V, A> {
/// Ensures a value is in the entry by inserting the default if empty, and returns
/// a mutable reference to the value in the entry.

View File

@ -152,3 +152,7 @@ trait SpecExtend<I: IntoIterator> {
/// Extends `self` with the contents of the given iterator.
fn spec_extend(&mut self, iter: I);
}
#[cfg(not(bootstrap))]
#[stable(feature = "try_reserve", since = "1.57.0")]
impl core::error::Error for TryReserveError {}

View File

@ -1121,3 +1121,29 @@ impl CStr {
CString::from(self)
}
}
#[cfg(not(bootstrap))]
#[stable(feature = "rust1", since = "1.0.0")]
impl core::error::Error for NulError {
#[allow(deprecated)]
fn description(&self) -> &str {
"nul byte found in data"
}
}
#[cfg(not(bootstrap))]
#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
impl core::error::Error for FromVecWithNulError {}
#[cfg(not(bootstrap))]
#[stable(feature = "cstring_into", since = "1.7.0")]
impl core::error::Error for IntoStringError {
#[allow(deprecated)]
fn description(&self) -> &str {
"C string contained non-utf8 bytes"
}
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
Some(self.__source())
}
}

View File

@ -111,6 +111,8 @@
#![feature(const_pin)]
#![feature(cstr_from_bytes_until_nul)]
#![feature(dispatch_from_dyn)]
#![cfg_attr(not(bootstrap), feature(error_generic_member_access))]
#![cfg_attr(not(bootstrap), feature(error_in_core))]
#![feature(exact_size_is_empty)]
#![feature(extend_one)]
#![feature(fmt_internals)]
@ -127,6 +129,7 @@
#![feature(nonnull_slice_from_raw_parts)]
#![feature(pattern)]
#![feature(pointer_byte_offsets)]
#![cfg_attr(not(bootstrap), feature(provide_any))]
#![feature(ptr_internals)]
#![feature(ptr_metadata)]
#![feature(ptr_sub_ptr)]
@ -179,6 +182,7 @@
#![feature(unboxed_closures)]
#![feature(unsized_fn_params)]
#![feature(c_unwind)]
#![feature(with_negative_coherence)]
//
// Rustdoc features:
#![feature(doc_cfg)]

View File

@ -44,6 +44,8 @@
#[cfg(not(no_global_oom_handling))]
use core::char::{decode_utf16, REPLACEMENT_CHARACTER};
#[cfg(not(bootstrap))]
use core::error::Error;
use core::fmt;
use core::hash;
use core::iter::FusedIterator;
@ -1939,6 +1941,24 @@ impl fmt::Display for FromUtf16Error {
}
}
#[cfg(not(bootstrap))]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for FromUtf8Error {
#[allow(deprecated)]
fn description(&self) -> &str {
"invalid utf-8"
}
}
#[cfg(not(bootstrap))]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for FromUtf16Error {
#[allow(deprecated)]
fn description(&self) -> &str {
"invalid utf-16"
}
}
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
impl Clone for String {

View File

@ -2763,3 +2763,25 @@ fn data_offset_align(align: usize) -> usize {
let layout = Layout::new::<ArcInner<()>>();
layout.size() + layout.padding_needed_for(align)
}
#[cfg(not(bootstrap))]
#[stable(feature = "arc_error", since = "1.52.0")]
impl<T: core::error::Error + ?Sized> core::error::Error for Arc<T> {
#[allow(deprecated, deprecated_in_future)]
fn description(&self) -> &str {
core::error::Error::description(&**self)
}
#[allow(deprecated)]
fn cause(&self) -> Option<&dyn core::error::Error> {
core::error::Error::cause(&**self)
}
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
core::error::Error::source(&**self)
}
fn provide<'a>(&'a self, req: &mut core::any::Demand<'a>) {
core::error::Error::provide(&**self, req);
}
}

View File

@ -5,6 +5,8 @@
// Your performance intuition is useless. Run perf.
use crate::cmp;
#[cfg(not(bootstrap))]
use crate::error::Error;
use crate::fmt;
use crate::mem::{self, ValidAlign};
use crate::ptr::NonNull;
@ -461,6 +463,10 @@ pub type LayoutErr = LayoutError;
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct LayoutError;
#[cfg(not(bootstrap))]
#[stable(feature = "alloc_layout", since = "1.28.0")]
impl Error for LayoutError {}
// (we need this for downstream impl of trait Error)
#[stable(feature = "alloc_layout", since = "1.28.0")]
impl fmt::Display for LayoutError {

View File

@ -21,6 +21,8 @@ pub use self::layout::LayoutErr;
#[stable(feature = "alloc_layout_error", since = "1.50.0")]
pub use self::layout::LayoutError;
#[cfg(not(bootstrap))]
use crate::error::Error;
use crate::fmt;
use crate::ptr::{self, NonNull};
@ -32,6 +34,14 @@ use crate::ptr::{self, NonNull};
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct AllocError;
#[cfg(not(bootstrap))]
#[unstable(
feature = "allocator_api",
reason = "the precise API and guarantees it provides may be tweaked.",
issue = "32838"
)]
impl Error for AllocError {}
// (we need this for downstream impl of trait Error)
#[unstable(feature = "allocator_api", issue = "32838")]
impl fmt::Display for AllocError {

View File

@ -7,6 +7,8 @@
use crate::borrow::{Borrow, BorrowMut};
use crate::cmp::Ordering;
use crate::convert::{Infallible, TryFrom};
#[cfg(not(bootstrap))]
use crate::error::Error;
use crate::fmt;
use crate::hash::{self, Hash};
use crate::iter::TrustedLen;
@ -119,6 +121,15 @@ impl fmt::Display for TryFromSliceError {
}
}
#[cfg(not(bootstrap))]
#[stable(feature = "try_from", since = "1.34.0")]
impl Error for TryFromSliceError {
#[allow(deprecated)]
fn description(&self) -> &str {
self.__description()
}
}
impl TryFromSliceError {
#[unstable(
feature = "array_error_internals",

View File

@ -1,5 +1,7 @@
//! UTF-8 and UTF-16 decoding iterators
#[cfg(not(bootstrap))]
use crate::error::Error;
use crate::fmt;
use super::from_u32_unchecked;
@ -121,3 +123,12 @@ impl fmt::Display for DecodeUtf16Error {
write!(f, "unpaired surrogate found: {:x}", self.code)
}
}
#[cfg(not(bootstrap))]
#[stable(feature = "decode_utf16", since = "1.9.0")]
impl Error for DecodeUtf16Error {
#[allow(deprecated)]
fn description(&self) -> &str {
"unpaired surrogate found"
}
}

View File

@ -38,6 +38,8 @@ pub use self::methods::encode_utf16_raw;
#[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")]
pub use self::methods::encode_utf8_raw;
#[cfg(not(bootstrap))]
use crate::error::Error;
use crate::fmt::{self, Write};
use crate::iter::FusedIterator;
@ -584,3 +586,7 @@ impl fmt::Display for TryFromCharError {
"unicode code point out of range".fmt(fmt)
}
}
#[cfg(not(bootstrap))]
#[stable(feature = "u8_from_char", since = "1.59.0")]
impl Error for TryFromCharError {}

View File

@ -34,6 +34,8 @@
#![stable(feature = "rust1", since = "1.0.0")]
#[cfg(not(bootstrap))]
use crate::error::Error;
use crate::fmt;
use crate::hash::{Hash, Hasher};
@ -715,6 +717,14 @@ impl fmt::Display for Infallible {
}
}
#[cfg(not(bootstrap))]
#[stable(feature = "str_parse_error2", since = "1.8.0")]
impl Error for Infallible {
fn description(&self) -> &str {
match *self {}
}
}
#[stable(feature = "convert_infallible", since = "1.34.0")]
impl PartialEq for Infallible {
fn eq(&self, _: &Infallible) -> bool {

137
library/core/src/error.md Normal file
View File

@ -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`]: ../../std/panic/fn.panic_any.html
[`PanicInfo`]: crate::panic::PanicInfo
[`catch_unwind`]: ../../std/panic/fn.catch_unwind.html
[`resume_unwind`]: ../../std/panic/fn.resume_unwind.html
[`downcast`]: crate::error::Error
[`Termination`]: ../../std/process/trait.Termination.html
[`Try`]: crate::ops::Try
[panic hook]: ../../std/panic/fn.set_hook.html
[`set_hook`]: ../../std/panic/fn.set_hook.html
[`take_hook`]: ../../std/panic/fn.take_hook.html
[panic-handler]: <https://doc.rust-lang.org/nomicon/panic-handler.html>
[`match`]: ../../std/keyword.match.html
[`?`]: ../../std/result/index.html#the-question-mark-operator-

508
library/core/src/error.rs Normal file
View File

@ -0,0 +1,508 @@
#![doc = include_str!("error.md")]
#![unstable(feature = "error_in_core", issue = "none")]
// A note about crates and the facade:
//
// Originally, the `Error` trait was defined in libcore, and the impls
// were scattered about. However, coherence objected to this
// arrangement, because to create the blanket impls for `Box` required
// knowing that `&str: !Error`, and we have no means to deal with that
// sort of conflict just now. Therefore, for the time being, we have
// moved the `Error` trait into libstd. As we evolve a sol'n to the
// coherence challenge (e.g., specialization, neg impls, etc) we can
// reconsider what crate these items belong in.
#[cfg(test)]
mod tests;
use crate::any::{Demand, Provider, TypeId};
use crate::fmt::{Debug, Display};
/// `Error` is a trait representing the basic expectations for error values,
/// i.e., values of type `E` in [`Result<T, E>`].
///
/// Errors must describe themselves through the [`Display`] and [`Debug`]
/// traits. Error messages are typically concise lowercase sentences without
/// trailing punctuation:
///
/// ```
/// let err = "NaN".parse::<u32>().unwrap_err();
/// assert_eq!(err.to_string(), "invalid digit found in string");
/// ```
///
/// Errors may provide cause chain information. [`Error::source()`] is generally
/// used when errors cross "abstraction boundaries". If one module must report
/// an error that is caused by an error from a lower-level module, it can allow
/// accessing that error via [`Error::source()`]. This makes it possible for the
/// high-level module to provide its own errors while also revealing some of the
/// implementation for debugging via `source` chains.
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "Error")]
#[rustc_has_incoherent_inherent_impls]
pub trait Error: Debug + Display {
/// The lower-level source of this error, if any.
///
/// # Examples
///
/// ```
/// use std::error::Error;
/// use std::fmt;
///
/// #[derive(Debug)]
/// struct SuperError {
/// source: SuperErrorSideKick,
/// }
///
/// impl fmt::Display for SuperError {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// write!(f, "SuperError is here!")
/// }
/// }
///
/// impl Error for SuperError {
/// fn source(&self) -> Option<&(dyn Error + 'static)> {
/// Some(&self.source)
/// }
/// }
///
/// #[derive(Debug)]
/// struct SuperErrorSideKick;
///
/// impl fmt::Display for SuperErrorSideKick {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// write!(f, "SuperErrorSideKick is here!")
/// }
/// }
///
/// impl Error for SuperErrorSideKick {}
///
/// fn get_super_error() -> Result<(), SuperError> {
/// Err(SuperError { source: SuperErrorSideKick })
/// }
///
/// fn main() {
/// match get_super_error() {
/// Err(e) => {
/// println!("Error: {e}");
/// println!("Caused by: {}", e.source().unwrap());
/// }
/// _ => println!("No error"),
/// }
/// }
/// ```
#[stable(feature = "error_source", since = "1.30.0")]
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
/// Gets the `TypeId` of `self`.
#[doc(hidden)]
#[unstable(
feature = "error_type_id",
reason = "this is memory-unsafe to override in user code",
issue = "60784"
)]
fn type_id(&self, _: private::Internal) -> TypeId
where
Self: 'static,
{
TypeId::of::<Self>()
}
/// ```
/// if let Err(e) = "xc".parse::<u32>() {
/// // Print `e` itself, no need for description().
/// eprintln!("Error: {e}");
/// }
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[deprecated(since = "1.42.0", note = "use the Display impl or to_string()")]
fn description(&self) -> &str {
"description() is deprecated; use Display"
}
#[stable(feature = "rust1", since = "1.0.0")]
#[deprecated(
since = "1.33.0",
note = "replaced by Error::source, which can support downcasting"
)]
#[allow(missing_docs)]
fn cause(&self) -> Option<&dyn Error> {
self.source()
}
/// Provides type based access to context intended for error reports.
///
/// Used in conjunction with [`Demand::provide_value`] and [`Demand::provide_ref`] to extract
/// references to member variables from `dyn Error` trait objects.
///
/// # Example
///
/// ```rust
/// #![feature(provide_any)]
/// #![feature(error_generic_member_access)]
/// use core::fmt;
/// use core::any::Demand;
///
/// #[derive(Debug)]
/// struct MyBacktrace {
/// // ...
/// }
///
/// impl MyBacktrace {
/// fn new() -> MyBacktrace {
/// // ...
/// # MyBacktrace {}
/// }
/// }
///
/// #[derive(Debug)]
/// struct SourceError {
/// // ...
/// }
///
/// impl fmt::Display for SourceError {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// write!(f, "Example Source Error")
/// }
/// }
///
/// impl std::error::Error for SourceError {}
///
/// #[derive(Debug)]
/// struct Error {
/// source: SourceError,
/// backtrace: MyBacktrace,
/// }
///
/// impl fmt::Display for Error {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// write!(f, "Example Error")
/// }
/// }
///
/// impl std::error::Error for Error {
/// fn provide<'a>(&'a self, req: &mut Demand<'a>) {
/// req
/// .provide_ref::<MyBacktrace>(&self.backtrace)
/// .provide_ref::<dyn std::error::Error + 'static>(&self.source);
/// }
/// }
///
/// fn main() {
/// let backtrace = MyBacktrace::new();
/// let source = SourceError {};
/// let error = Error { source, backtrace };
/// let dyn_error = &error as &dyn std::error::Error;
/// let backtrace_ref = dyn_error.request_ref::<MyBacktrace>().unwrap();
///
/// assert!(core::ptr::eq(&error.backtrace, backtrace_ref));
/// }
/// ```
#[unstable(feature = "error_generic_member_access", issue = "99301")]
#[allow(unused_variables)]
fn provide<'a>(&'a self, req: &mut Demand<'a>) {}
}
#[unstable(feature = "error_generic_member_access", issue = "99301")]
impl<E> Provider for E
where
E: Error + ?Sized,
{
fn provide<'a>(&'a self, req: &mut Demand<'a>) {
self.provide(req)
}
}
mod private {
// This is a hack to prevent `type_id` from being overridden by `Error`
// implementations, since that can enable unsound downcasting.
#[unstable(feature = "error_type_id", issue = "60784")]
#[derive(Debug)]
pub struct Internal;
}
#[unstable(feature = "never_type", issue = "35121")]
impl Error for ! {}
impl<'a> dyn Error + 'a {
/// Request a reference of type `T` as context about this error.
#[unstable(feature = "error_generic_member_access", issue = "99301")]
pub fn request_ref<T: ?Sized + 'static>(&'a self) -> Option<&'a T> {
core::any::request_ref(self)
}
/// Request a value of type `T` as context about this error.
#[unstable(feature = "error_generic_member_access", issue = "99301")]
pub fn request_value<T: 'static>(&'a self) -> Option<T> {
core::any::request_value(self)
}
}
// Copied from `any.rs`.
impl dyn Error + 'static {
/// Returns `true` if the inner type is the same as `T`.
#[stable(feature = "error_downcast", since = "1.3.0")]
#[inline]
pub fn is<T: Error + 'static>(&self) -> bool {
// Get `TypeId` of the type this function is instantiated with.
let t = TypeId::of::<T>();
// Get `TypeId` of the type in the trait object (`self`).
let concrete = self.type_id(private::Internal);
// Compare both `TypeId`s on equality.
t == concrete
}
/// Returns some reference to the inner value if it is of type `T`, or
/// `None` if it isn't.
#[stable(feature = "error_downcast", since = "1.3.0")]
#[inline]
pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
if self.is::<T>() {
// SAFETY: `is` ensures this type cast is correct
unsafe { Some(&*(self as *const dyn Error as *const T)) }
} else {
None
}
}
/// Returns some mutable reference to the inner value if it is of type `T`, or
/// `None` if it isn't.
#[stable(feature = "error_downcast", since = "1.3.0")]
#[inline]
pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> {
if self.is::<T>() {
// SAFETY: `is` ensures this type cast is correct
unsafe { Some(&mut *(self as *mut dyn Error as *mut T)) }
} else {
None
}
}
}
impl dyn Error + 'static + Send {
/// Forwards to the method defined on the type `dyn Error`.
#[stable(feature = "error_downcast", since = "1.3.0")]
#[inline]
pub fn is<T: Error + 'static>(&self) -> bool {
<dyn Error + 'static>::is::<T>(self)
}
/// Forwards to the method defined on the type `dyn Error`.
#[stable(feature = "error_downcast", since = "1.3.0")]
#[inline]
pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
<dyn Error + 'static>::downcast_ref::<T>(self)
}
/// Forwards to the method defined on the type `dyn Error`.
#[stable(feature = "error_downcast", since = "1.3.0")]
#[inline]
pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> {
<dyn Error + 'static>::downcast_mut::<T>(self)
}
/// Request a reference of type `T` as context about this error.
#[unstable(feature = "error_generic_member_access", issue = "99301")]
pub fn request_ref<T: ?Sized + 'static>(&self) -> Option<&T> {
<dyn Error>::request_ref(self)
}
/// Request a value of type `T` as context about this error.
#[unstable(feature = "error_generic_member_access", issue = "99301")]
pub fn request_value<T: 'static>(&self) -> Option<T> {
<dyn Error>::request_value(self)
}
}
impl dyn Error + 'static + Send + Sync {
/// Forwards to the method defined on the type `dyn Error`.
#[stable(feature = "error_downcast", since = "1.3.0")]
#[inline]
pub fn is<T: Error + 'static>(&self) -> bool {
<dyn Error + 'static>::is::<T>(self)
}
/// Forwards to the method defined on the type `dyn Error`.
#[stable(feature = "error_downcast", since = "1.3.0")]
#[inline]
pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
<dyn Error + 'static>::downcast_ref::<T>(self)
}
/// Forwards to the method defined on the type `dyn Error`.
#[stable(feature = "error_downcast", since = "1.3.0")]
#[inline]
pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> {
<dyn Error + 'static>::downcast_mut::<T>(self)
}
/// Request a reference of type `T` as context about this error.
#[unstable(feature = "error_generic_member_access", issue = "99301")]
pub fn request_ref<T: ?Sized + 'static>(&self) -> Option<&T> {
<dyn Error>::request_ref(self)
}
/// Request a value of type `T` as context about this error.
#[unstable(feature = "error_generic_member_access", issue = "99301")]
pub fn request_value<T: 'static>(&self) -> Option<T> {
<dyn Error>::request_value(self)
}
}
impl dyn Error {
/// Returns an iterator starting with the current error and continuing with
/// recursively calling [`Error::source`].
///
/// If you want to omit the current error and only use its sources,
/// use `skip(1)`.
///
/// # Examples
///
/// ```
/// #![feature(error_iter)]
/// use std::error::Error;
/// use std::fmt;
///
/// #[derive(Debug)]
/// struct A;
///
/// #[derive(Debug)]
/// struct B(Option<Box<dyn Error + 'static>>);
///
/// impl fmt::Display for A {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// write!(f, "A")
/// }
/// }
///
/// impl fmt::Display for B {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// write!(f, "B")
/// }
/// }
///
/// impl Error for A {}
///
/// impl Error for B {
/// fn source(&self) -> Option<&(dyn Error + 'static)> {
/// self.0.as_ref().map(|e| e.as_ref())
/// }
/// }
///
/// let b = B(Some(Box::new(A)));
///
/// // let err : Box<Error> = b.into(); // or
/// let err = &b as &(dyn Error);
///
/// let mut iter = err.chain();
///
/// assert_eq!("B".to_string(), iter.next().unwrap().to_string());
/// assert_eq!("A".to_string(), iter.next().unwrap().to_string());
/// assert!(iter.next().is_none());
/// assert!(iter.next().is_none());
/// ```
#[unstable(feature = "error_iter", issue = "58520")]
#[inline]
pub fn chain(&self) -> Chain<'_> {
Chain { current: Some(self) }
}
}
/// An iterator over an [`Error`] and its sources.
///
/// If you want to omit the initial error and only process
/// its sources, use `skip(1)`.
#[unstable(feature = "error_iter", issue = "58520")]
#[derive(Clone, Debug)]
pub struct Chain<'a> {
current: Option<&'a (dyn Error + 'static)>,
}
#[unstable(feature = "error_iter", issue = "58520")]
impl<'a> Iterator for Chain<'a> {
type Item = &'a (dyn Error + 'static);
fn next(&mut self) -> Option<Self::Item> {
let current = self.current;
self.current = self.current.and_then(Error::source);
current
}
}
#[stable(feature = "error_by_ref", since = "1.51.0")]
impl<'a, T: Error + ?Sized> Error for &'a T {
#[allow(deprecated, deprecated_in_future)]
fn description(&self) -> &str {
Error::description(&**self)
}
#[allow(deprecated)]
fn cause(&self) -> Option<&dyn Error> {
Error::cause(&**self)
}
fn source(&self) -> Option<&(dyn Error + 'static)> {
Error::source(&**self)
}
fn provide<'b>(&'b self, req: &mut Demand<'b>) {
Error::provide(&**self, req);
}
}
#[stable(feature = "fmt_error", since = "1.11.0")]
impl Error for crate::fmt::Error {
#[allow(deprecated)]
fn description(&self) -> &str {
"an error occurred when formatting an argument"
}
}
#[stable(feature = "try_borrow", since = "1.13.0")]
impl Error for crate::cell::BorrowError {
#[allow(deprecated)]
fn description(&self) -> &str {
"already mutably borrowed"
}
}
#[stable(feature = "try_borrow", since = "1.13.0")]
impl Error for crate::cell::BorrowMutError {
#[allow(deprecated)]
fn description(&self) -> &str {
"already borrowed"
}
}
#[stable(feature = "try_from", since = "1.34.0")]
impl Error for crate::char::CharTryFromError {
#[allow(deprecated)]
fn description(&self) -> &str {
"converted integer out of range for `char`"
}
}
#[stable(feature = "char_from_str", since = "1.20.0")]
impl Error for crate::char::ParseCharError {
#[allow(deprecated)]
fn description(&self) -> &str {
self.__description()
}
}
#[unstable(feature = "duration_checked_float", issue = "83400")]
impl Error for crate::time::FromFloatSecsError {}
#[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")]
impl Error for crate::ffi::FromBytesWithNulError {
#[allow(deprecated)]
fn description(&self) -> &str {
self.__description()
}
}
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
impl Error for crate::ffi::FromBytesUntilNulError {}

View File

@ -302,6 +302,8 @@ pub mod clone;
pub mod cmp;
pub mod convert;
pub mod default;
#[cfg(not(bootstrap))]
pub mod error;
pub mod marker;
pub mod ops;

View File

@ -1,6 +1,8 @@
//! Error types for conversion to integral types.
use crate::convert::Infallible;
#[cfg(not(bootstrap))]
use crate::error::Error;
use crate::fmt;
/// The error type returned when a checked integral type conversion fails.
@ -144,3 +146,21 @@ impl fmt::Display for ParseIntError {
self.__description().fmt(f)
}
}
#[cfg(not(bootstrap))]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for ParseIntError {
#[allow(deprecated)]
fn description(&self) -> &str {
self.__description()
}
}
#[cfg(not(bootstrap))]
#[stable(feature = "try_from", since = "1.34.0")]
impl Error for TryFromIntError {
#[allow(deprecated)]
fn description(&self) -> &str {
self.__description()
}
}

View File

@ -3,6 +3,8 @@
#![stable(feature = "rust1", since = "1.0.0")]
use crate::ascii;
#[cfg(not(bootstrap))]
use crate::error::Error;
use crate::intrinsics;
use crate::mem;
use crate::ops::{Add, Mul, Sub};
@ -57,6 +59,16 @@ pub use wrapping::Wrapping;
#[cfg(not(no_fp_fmt_parse))]
pub use dec2flt::ParseFloatError;
#[cfg(not(bootstrap))]
#[cfg(not(no_fp_fmt_parse))]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for ParseFloatError {
#[allow(deprecated)]
fn description(&self) -> &str {
self.__description()
}
}
#[stable(feature = "rust1", since = "1.0.0")]
pub use error::ParseIntError;

View File

@ -1,5 +1,7 @@
//! Defines utf8 error type.
#[cfg(not(bootstrap))]
use crate::error::Error;
use crate::fmt;
/// Errors which can occur when attempting to interpret a sequence of [`u8`]
@ -122,6 +124,15 @@ impl fmt::Display for Utf8Error {
}
}
#[cfg(not(bootstrap))]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for Utf8Error {
#[allow(deprecated)]
fn description(&self) -> &str {
"invalid utf-8: corrupt contents"
}
}
/// An error returned when parsing a `bool` using [`from_str`] fails
///
/// [`from_str`]: super::FromStr::from_str
@ -136,3 +147,12 @@ impl fmt::Display for ParseBoolError {
"provided string was not `true` or `false`".fmt(f)
}
}
#[cfg(not(bootstrap))]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for ParseBoolError {
#[allow(deprecated)]
fn description(&self) -> &str {
"failed to parse bool"
}
}

View File

@ -2638,3 +2638,7 @@ impl_fn_for_zst! {
unsafe { from_utf8_unchecked(bytes) }
};
}
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg(not(bootstrap))]
impl !crate::error::Error for &str {}

View File

@ -9,6 +9,8 @@ use crate::borrow::Borrow;
use crate::cell::Cell;
use crate::collections::TryReserveError;
use crate::collections::TryReserveErrorKind;
#[cfg(not(bootstrap))]
use crate::error::Error;
use crate::fmt::{self, Debug};
#[allow(deprecated)]
use crate::hash::{BuildHasher, Hash, Hasher, SipHasher13};
@ -2158,6 +2160,15 @@ impl<'a, K: Debug, V: Debug> fmt::Display for OccupiedError<'a, K, V> {
}
}
#[cfg(not(bootstrap))]
#[unstable(feature = "map_try_insert", issue = "82766")]
impl<'a, K: fmt::Debug, V: fmt::Debug> Error for OccupiedError<'a, K, V> {
#[allow(deprecated)]
fn description(&self) -> &str {
"key already exists"
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S> {
type Item = (&'a K, &'a V);

View File

@ -1,141 +1,4 @@
//! The `Error` trait provides common functionality for 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:
@ -152,24 +15,48 @@
#[cfg(test)]
mod tests;
#[cfg(bootstrap)]
use core::array;
#[cfg(bootstrap)]
use core::convert::Infallible;
#[cfg(bootstrap)]
use crate::alloc::{AllocError, LayoutError};
use crate::any::{Demand, Provider, TypeId};
#[cfg(bootstrap)]
use crate::any::Demand;
#[cfg(bootstrap)]
use crate::any::{Provider, TypeId};
use crate::backtrace::Backtrace;
#[cfg(bootstrap)]
use crate::borrow::Cow;
#[cfg(bootstrap)]
use crate::cell;
#[cfg(bootstrap)]
use crate::char;
use crate::fmt::{self, Debug, Display, Write};
#[cfg(bootstrap)]
use crate::fmt::Debug;
#[cfg(bootstrap)]
use crate::fmt::Display;
use crate::fmt::{self, Write};
#[cfg(bootstrap)]
use crate::io;
#[cfg(bootstrap)]
use crate::mem::transmute;
#[cfg(bootstrap)]
use crate::num;
#[cfg(bootstrap)]
use crate::str;
#[cfg(bootstrap)]
use crate::string;
#[cfg(bootstrap)]
use crate::sync::Arc;
#[cfg(bootstrap)]
use crate::time;
#[cfg(not(bootstrap))]
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::error::Error;
/// `Error` is a trait representing the basic expectations for error values,
/// i.e., values of type `E` in [`Result<T, E>`].
///
@ -190,6 +77,7 @@ use crate::time;
/// implementation for debugging via `source` chains.
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "Error")]
#[cfg(bootstrap)]
pub trait Error: Debug + Display {
/// The lower-level source of this error, if any.
///
@ -355,6 +243,7 @@ pub trait Error: Debug + Display {
fn provide<'a>(&'a self, req: &mut Demand<'a>) {}
}
#[cfg(bootstrap)]
#[unstable(feature = "error_generic_member_access", issue = "99301")]
impl<'b> Provider for dyn Error + 'b {
fn provide<'a>(&'a self, req: &mut Demand<'a>) {
@ -370,6 +259,7 @@ mod private {
pub struct Internal;
}
#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, E: Error + 'a> From<E> for Box<dyn Error + 'a> {
/// Converts a type of [`Error`] into a box of dyn [`Error`].
@ -402,6 +292,7 @@ impl<'a, E: Error + 'a> From<E> for Box<dyn Error + 'a> {
}
}
#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, E: Error + Send + Sync + 'a> From<E> for Box<dyn Error + Send + Sync + 'a> {
/// Converts a type of [`Error`] + [`Send`] + [`Sync`] into a box of
@ -440,6 +331,7 @@ impl<'a, E: Error + Send + Sync + 'a> From<E> for Box<dyn Error + Send + Sync +
}
}
#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl From<String> for Box<dyn Error + Send + Sync> {
/// Converts a [`String`] into a box of dyn [`Error`] + [`Send`] + [`Sync`].
@ -483,6 +375,7 @@ impl From<String> for Box<dyn Error + Send + Sync> {
}
}
#[cfg(bootstrap)]
#[stable(feature = "string_box_error", since = "1.6.0")]
impl From<String> for Box<dyn Error> {
/// Converts a [`String`] into a box of dyn [`Error`].
@ -504,6 +397,7 @@ impl From<String> for Box<dyn Error> {
}
}
#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> From<&str> for Box<dyn Error + Send + Sync + 'a> {
/// Converts a [`str`] into a box of dyn [`Error`] + [`Send`] + [`Sync`].
@ -527,6 +421,7 @@ impl<'a> From<&str> for Box<dyn Error + Send + Sync + 'a> {
}
}
#[cfg(bootstrap)]
#[stable(feature = "string_box_error", since = "1.6.0")]
impl From<&str> for Box<dyn Error> {
/// Converts a [`str`] into a box of dyn [`Error`].
@ -548,6 +443,7 @@ impl From<&str> for Box<dyn Error> {
}
}
#[cfg(bootstrap)]
#[stable(feature = "cow_box_error", since = "1.22.0")]
impl<'a, 'b> From<Cow<'b, str>> for Box<dyn Error + Send + Sync + 'a> {
/// Converts a [`Cow`] into a box of dyn [`Error`] + [`Send`] + [`Sync`].
@ -569,6 +465,7 @@ impl<'a, 'b> From<Cow<'b, str>> for Box<dyn Error + Send + Sync + 'a> {
}
}
#[cfg(bootstrap)]
#[stable(feature = "cow_box_error", since = "1.22.0")]
impl<'a> From<Cow<'a, str>> for Box<dyn Error> {
/// Converts a [`Cow`] into a box of dyn [`Error`].
@ -589,9 +486,11 @@ impl<'a> From<Cow<'a, str>> for Box<dyn Error> {
}
}
#[cfg(bootstrap)]
#[unstable(feature = "never_type", issue = "35121")]
impl Error for ! {}
#[cfg(bootstrap)]
#[unstable(
feature = "allocator_api",
reason = "the precise API and guarantees it provides may be tweaked.",
@ -599,9 +498,11 @@ impl Error for ! {}
)]
impl Error for AllocError {}
#[cfg(bootstrap)]
#[stable(feature = "alloc_layout", since = "1.28.0")]
impl Error for LayoutError {}
#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for str::ParseBoolError {
#[allow(deprecated)]
@ -610,6 +511,7 @@ impl Error for str::ParseBoolError {
}
}
#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for str::Utf8Error {
#[allow(deprecated)]
@ -618,6 +520,7 @@ impl Error for str::Utf8Error {
}
}
#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for num::ParseIntError {
#[allow(deprecated)]
@ -626,6 +529,7 @@ impl Error for num::ParseIntError {
}
}
#[cfg(bootstrap)]
#[stable(feature = "try_from", since = "1.34.0")]
impl Error for num::TryFromIntError {
#[allow(deprecated)]
@ -634,6 +538,7 @@ impl Error for num::TryFromIntError {
}
}
#[cfg(bootstrap)]
#[stable(feature = "try_from", since = "1.34.0")]
impl Error for array::TryFromSliceError {
#[allow(deprecated)]
@ -642,6 +547,7 @@ impl Error for array::TryFromSliceError {
}
}
#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for num::ParseFloatError {
#[allow(deprecated)]
@ -650,6 +556,7 @@ impl Error for num::ParseFloatError {
}
}
#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for string::FromUtf8Error {
#[allow(deprecated)]
@ -658,6 +565,7 @@ impl Error for string::FromUtf8Error {
}
}
#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for string::FromUtf16Error {
#[allow(deprecated)]
@ -666,6 +574,7 @@ impl Error for string::FromUtf16Error {
}
}
#[cfg(bootstrap)]
#[stable(feature = "str_parse_error2", since = "1.8.0")]
impl Error for Infallible {
fn description(&self) -> &str {
@ -673,6 +582,7 @@ impl Error for Infallible {
}
}
#[cfg(bootstrap)]
#[stable(feature = "decode_utf16", since = "1.9.0")]
impl Error for char::DecodeUtf16Error {
#[allow(deprecated)]
@ -681,9 +591,11 @@ impl Error for char::DecodeUtf16Error {
}
}
#[cfg(bootstrap)]
#[stable(feature = "u8_from_char", since = "1.59.0")]
impl Error for char::TryFromCharError {}
#[cfg(bootstrap)]
#[unstable(feature = "map_try_insert", issue = "82766")]
impl<'a, K: Debug + Ord, V: Debug> Error
for crate::collections::btree_map::OccupiedError<'a, K, V>
@ -694,6 +606,7 @@ impl<'a, K: Debug + Ord, V: Debug> Error
}
}
#[cfg(bootstrap)]
#[unstable(feature = "map_try_insert", issue = "82766")]
impl<'a, K: Debug, V: Debug> Error for crate::collections::hash_map::OccupiedError<'a, K, V> {
#[allow(deprecated)]
@ -702,6 +615,7 @@ impl<'a, K: Debug, V: Debug> Error for crate::collections::hash_map::OccupiedErr
}
}
#[cfg(bootstrap)]
#[stable(feature = "box_error", since = "1.8.0")]
impl<T: Error> Error for Box<T> {
#[allow(deprecated, deprecated_in_future)]
@ -719,6 +633,7 @@ impl<T: Error> Error for Box<T> {
}
}
#[cfg(bootstrap)]
#[unstable(feature = "thin_box", issue = "92791")]
impl<T: ?Sized + crate::error::Error> crate::error::Error for crate::boxed::ThinBox<T> {
fn source(&self) -> Option<&(dyn crate::error::Error + 'static)> {
@ -727,6 +642,7 @@ impl<T: ?Sized + crate::error::Error> crate::error::Error for crate::boxed::Thin
}
}
#[cfg(bootstrap)]
#[stable(feature = "error_by_ref", since = "1.51.0")]
impl<'a, T: Error + ?Sized> Error for &'a T {
#[allow(deprecated, deprecated_in_future)]
@ -748,6 +664,7 @@ impl<'a, T: Error + ?Sized> Error for &'a T {
}
}
#[cfg(bootstrap)]
#[stable(feature = "arc_error", since = "1.52.0")]
impl<T: Error + ?Sized> Error for Arc<T> {
#[allow(deprecated, deprecated_in_future)]
@ -769,6 +686,7 @@ impl<T: Error + ?Sized> Error for Arc<T> {
}
}
#[cfg(bootstrap)]
#[stable(feature = "fmt_error", since = "1.11.0")]
impl Error for fmt::Error {
#[allow(deprecated)]
@ -777,6 +695,7 @@ impl Error for fmt::Error {
}
}
#[cfg(bootstrap)]
#[stable(feature = "try_borrow", since = "1.13.0")]
impl Error for cell::BorrowError {
#[allow(deprecated)]
@ -785,6 +704,7 @@ impl Error for cell::BorrowError {
}
}
#[cfg(bootstrap)]
#[stable(feature = "try_borrow", since = "1.13.0")]
impl Error for cell::BorrowMutError {
#[allow(deprecated)]
@ -793,6 +713,7 @@ impl Error for cell::BorrowMutError {
}
}
#[cfg(bootstrap)]
#[stable(feature = "try_from", since = "1.34.0")]
impl Error for char::CharTryFromError {
#[allow(deprecated)]
@ -801,6 +722,7 @@ impl Error for char::CharTryFromError {
}
}
#[cfg(bootstrap)]
#[stable(feature = "char_from_str", since = "1.20.0")]
impl Error for char::ParseCharError {
#[allow(deprecated)]
@ -809,12 +731,15 @@ impl Error for char::ParseCharError {
}
}
#[cfg(bootstrap)]
#[stable(feature = "try_reserve", since = "1.57.0")]
impl Error for alloc::collections::TryReserveError {}
#[cfg(bootstrap)]
#[unstable(feature = "duration_checked_float", issue = "83400")]
impl Error for time::FromFloatSecsError {}
#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl Error for alloc::ffi::NulError {
#[allow(deprecated)]
@ -823,6 +748,7 @@ impl Error for alloc::ffi::NulError {
}
}
#[cfg(bootstrap)]
#[stable(feature = "rust1", since = "1.0.0")]
impl From<alloc::ffi::NulError> for io::Error {
/// Converts a [`alloc::ffi::NulError`] into a [`io::Error`].
@ -831,6 +757,7 @@ impl From<alloc::ffi::NulError> for io::Error {
}
}
#[cfg(bootstrap)]
#[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")]
impl Error for core::ffi::FromBytesWithNulError {
#[allow(deprecated)]
@ -839,12 +766,15 @@ impl Error for core::ffi::FromBytesWithNulError {
}
}
#[cfg(bootstrap)]
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
impl Error for core::ffi::FromBytesUntilNulError {}
#[cfg(bootstrap)]
#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
impl Error for alloc::ffi::FromVecWithNulError {}
#[cfg(bootstrap)]
#[stable(feature = "cstring_into", since = "1.7.0")]
impl Error for alloc::ffi::IntoStringError {
#[allow(deprecated)]
@ -857,6 +787,7 @@ impl Error for alloc::ffi::IntoStringError {
}
}
#[cfg(bootstrap)]
impl<'a> dyn Error + 'a {
/// Request a reference of type `T` as context about this error.
#[unstable(feature = "error_generic_member_access", issue = "99301")]
@ -872,6 +803,7 @@ impl<'a> dyn Error + 'a {
}
// Copied from `any.rs`.
#[cfg(bootstrap)]
impl dyn Error + 'static {
/// Returns `true` if the inner type is the same as `T`.
#[stable(feature = "error_downcast", since = "1.3.0")]
@ -912,6 +844,7 @@ impl dyn Error + 'static {
}
}
#[cfg(bootstrap)]
impl dyn Error + 'static + Send {
/// Forwards to the method defined on the type `dyn Error`.
#[stable(feature = "error_downcast", since = "1.3.0")]
@ -947,6 +880,7 @@ impl dyn Error + 'static + Send {
}
}
#[cfg(bootstrap)]
impl dyn Error + 'static + Send + Sync {
/// Forwards to the method defined on the type `dyn Error`.
#[stable(feature = "error_downcast", since = "1.3.0")]
@ -982,6 +916,7 @@ impl dyn Error + 'static + Send + Sync {
}
}
#[cfg(bootstrap)]
impl dyn Error {
#[inline]
#[stable(feature = "error_downcast", since = "1.3.0")]
@ -1061,10 +996,12 @@ impl dyn Error {
/// its sources, use `skip(1)`.
#[unstable(feature = "error_iter", issue = "58520")]
#[derive(Clone, Debug)]
#[cfg(bootstrap)]
pub struct Chain<'a> {
current: Option<&'a (dyn Error + 'static)>,
}
#[cfg(bootstrap)]
#[unstable(feature = "error_iter", issue = "58520")]
impl<'a> Iterator for Chain<'a> {
type Item = &'a (dyn Error + 'static);
@ -1076,6 +1013,7 @@ impl<'a> Iterator for Chain<'a> {
}
}
#[cfg(bootstrap)]
impl dyn Error + Send {
#[inline]
#[stable(feature = "error_downcast", since = "1.3.0")]
@ -1089,6 +1027,7 @@ impl dyn Error + Send {
}
}
#[cfg(bootstrap)]
impl dyn Error + Send + Sync {
#[inline]
#[stable(feature = "error_downcast", since = "1.3.0")]
@ -1246,7 +1185,7 @@ impl dyn Error + Send + Sync {
/// # Err(SuperError { source: SuperErrorSideKick })
/// # }
///
/// fn main() -> Result<(), Report> {
/// fn main() -> Result<(), Report<SuperError>> {
/// get_super_error()?;
/// Ok(())
/// }
@ -1293,7 +1232,7 @@ impl dyn Error + Send + Sync {
/// # Err(SuperError { source: SuperErrorSideKick })
/// # }
///
/// fn main() -> Result<(), Report> {
/// fn main() -> Result<(), Report<SuperError>> {
/// get_super_error()
/// .map_err(Report::from)
/// .map_err(|r| r.pretty(true).show_backtrace(true))?;
@ -1605,72 +1544,6 @@ where
}
}
impl Report<Box<dyn Error>> {
fn backtrace(&self) -> Option<&Backtrace> {
// have to grab the backtrace on the first error directly since that error may not be
// 'static
let backtrace = self.error.request_ref();
let backtrace = backtrace.or_else(|| {
self.error
.source()
.map(|source| source.chain().find_map(|source| source.request_ref()))
.flatten()
});
backtrace
}
/// Format the report as a single line.
#[unstable(feature = "error_reporter", issue = "90172")]
fn fmt_singleline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.error)?;
let sources = self.error.source().into_iter().flat_map(<dyn Error>::chain);
for cause in sources {
write!(f, ": {cause}")?;
}
Ok(())
}
/// Format the report as multiple lines, with each error cause on its own line.
#[unstable(feature = "error_reporter", issue = "90172")]
fn fmt_multiline(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let error = &self.error;
write!(f, "{error}")?;
if let Some(cause) = error.source() {
write!(f, "\n\nCaused by:")?;
let multiple = cause.source().is_some();
for (ind, error) in cause.chain().enumerate() {
writeln!(f)?;
let mut indented = Indented { inner: f };
if multiple {
write!(indented, "{ind: >4}: {error}")?;
} else {
write!(indented, " {error}")?;
}
}
}
if self.show_backtrace {
let backtrace = self.backtrace();
if let Some(backtrace) = backtrace {
let backtrace = backtrace.to_string();
f.write_str("\n\nStack backtrace:\n")?;
f.write_str(backtrace.trim_end())?;
}
}
Ok(())
}
}
#[unstable(feature = "error_reporter", issue = "90172")]
impl<E> From<E> for Report<E>
where
@ -1681,17 +1554,6 @@ where
}
}
#[unstable(feature = "error_reporter", issue = "90172")]
impl<'a, E> From<E> for Report<Box<dyn Error + 'a>>
where
E: Error + 'a,
{
fn from(error: E) -> Self {
let error = box error;
Report { error, show_backtrace: false, pretty: false }
}
}
#[unstable(feature = "error_reporter", issue = "90172")]
impl<E> fmt::Display for Report<E>
where
@ -1702,13 +1564,6 @@ where
}
}
#[unstable(feature = "error_reporter", issue = "90172")]
impl fmt::Display for Report<Box<dyn Error>> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.pretty { self.fmt_multiline(f) } else { self.fmt_singleline(f) }
}
}
// This type intentionally outputs the same format for `Display` and `Debug`for
// situations where you unwrap a `Report` or return it from main.
#[unstable(feature = "error_reporter", issue = "90172")]

View File

@ -76,6 +76,15 @@ impl fmt::Debug for Error {
}
}
#[cfg(not(bootstrap))]
#[stable(feature = "rust1", since = "1.0.0")]
impl From<alloc::ffi::NulError> for Error {
/// Converts a [`alloc::ffi::NulError`] into a [`Error`].
fn from(_: alloc::ffi::NulError) -> Error {
const_io_error!(ErrorKind::InvalidInput, "data provided contains a nul byte")
}
}
// Only derive debug in tests, to make sure it
// doesn't accidentally get printed.
#[cfg_attr(test, derive(Debug))]

View File

@ -281,6 +281,9 @@
#![feature(cstr_internals)]
#![feature(duration_checked_float)]
#![feature(duration_constants)]
#![cfg_attr(not(bootstrap), feature(error_generic_member_access))]
#![cfg_attr(not(bootstrap), feature(error_in_core))]
#![cfg_attr(not(bootstrap), feature(error_iter))]
#![feature(exact_size_is_empty)]
#![feature(exclusive_wrapper)]
#![feature(extend_one)]