#
std/
panic.rs

    1//! Panic support in the standard library.
    2
    3#![stable(feature = "std_panic", since = "1.9.0")]
    4
    5use crate::any::Any;
    6use crate::sync::atomic::{Atomic, AtomicU8, Ordering};
    7use crate::sync::{Condvar, Mutex, RwLock};
    8use crate::thread::Result;
    9use crate::{collections, fmt, panicking};
    10
    11#[stable(feature = "panic_hooks", since = "1.10.0")]
    12#[deprecated(
    13    since = "1.82.0",
    14    note = "use `PanicHookInfo` instead",
    15    suggestion = "std::panic::PanicHookInfo"
    16)]
    17/// A struct providing information about a panic.
    18///
    19/// `PanicInfo` has been renamed to [`PanicHookInfo`] to avoid confusion with
    20/// [`core::panic::PanicInfo`].
    21pub type PanicInfo<'a> = PanicHookInfo<'a>;
    22
    23/// A struct providing information about a panic.
    24///
    25/// `PanicHookInfo` structure is passed to a panic hook set by the [`set_hook`] function.
    26///
    27/// # Examples
    28///
    29/// ```should_panic
    30/// use std::panic;
    31///
    32/// panic::set_hook(Box::new(|panic_info| {
    33///     println!("panic occurred: {panic_info}");
    34/// }));
    35///
    36/// panic!("critical system failure");
    37/// ```
    38///
    39/// [`set_hook`]: ../../std/panic/fn.set_hook.html
    40#[stable(feature = "panic_hook_info", since = "1.81.0")]
    41#[derive(Debug)]
    42pub struct PanicHookInfo<'a> {
    43    payload: &'a (dyn Any + Send),
    44    location: &'a Location<'a>,
    45    can_unwind: bool,
    46    force_no_backtrace: bool,
    47}
    48
    49impl<'a> PanicHookInfo<'a> {
    50    #[inline]
    51    pub(crate) fn new(
    52        location: &'a Location<'a>,
    53        payload: &'a (dyn Any + Send),
    54        can_unwind: bool,
    55        force_no_backtrace: bool,
    56    ) -> Self {
    57        PanicHookInfo { payload, location, can_unwind, force_no_backtrace }
    58    }
    59
    60    /// Returns the payload associated with the panic.
    61    ///
    62    /// This will commonly, but not always, be a `&'static str` or [`String`].
    63    /// If you only care about such payloads, use [`payload_as_str`] instead.
    64    ///
    65    /// A invocation of the `panic!()` macro in Rust 2021 or later will always result in a
    66    /// panic payload of type `&'static str` or `String`.
    67    ///
    68    /// Only an invocation of [`panic_any`]
    69    /// (or, in Rust 2018 and earlier, `panic!(x)` where `x` is something other than a string)
    70    /// can result in a panic payload other than a `&'static str` or `String`.
    71    ///
    72    /// [`String`]: ../../std/string/struct.String.html
    73    /// [`payload_as_str`]: PanicHookInfo::payload_as_str
    74    ///
    75    /// # Examples
    76    ///
    77    /// ```should_panic
    78    /// use std::panic;
    79    ///
    80    /// panic::set_hook(Box::new(|panic_info| {
    81    ///     if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
    82    ///         println!("panic occurred: {s:?}");
    83    ///     } else if let Some(s) = panic_info.payload().downcast_ref::<String>() {
    84    ///         println!("panic occurred: {s:?}");
    85    ///     } else {
    86    ///         println!("panic occurred");
    87    ///     }
    88    /// }));
    89    ///
    90    /// panic!("Normal panic");
    91    /// ```
    92    #[must_use]
    93    #[inline]
    94    #[stable(feature = "panic_hooks", since = "1.10.0")]
    95    pub fn payload(&self) -> &(dyn Any + Send) {
    96        self.payload
    97    }
    98
    99    /// Returns the payload associated with the panic, if it is a string.
    100    ///
    101    /// This returns the payload if it is of type `&'static str` or `String`.
    102    ///
    103    /// A invocation of the `panic!()` macro in Rust 2021 or later will always result in a
    104    /// panic payload where `payload_as_str` returns `Some`.
    105    ///
    106    /// Only an invocation of [`panic_any`]
    107    /// (or, in Rust 2018 and earlier, `panic!(x)` where `x` is something other than a string)
    108    /// can result in a panic payload where `payload_as_str` returns `None`.
    109    ///
    110    /// # Example
    111    ///
    112    /// ```should_panic
    113    /// std::panic::set_hook(Box::new(|panic_info| {
    114    ///     if let Some(s) = panic_info.payload_as_str() {
    115    ///         println!("panic occurred: {s:?}");
    116    ///     } else {
    117    ///         println!("panic occurred");
    118    ///     }
    119    /// }));
    120    ///
    121    /// panic!("Normal panic");
    122    /// ```
    123    #[must_use]
    124    #[inline]
    125    #[stable(feature = "panic_payload_as_str", since = "CURRENT_RUSTC_VERSION")]
    126    pub fn payload_as_str(&self) -> Option<&str> {
    127        if let Some(s) = self.payload.downcast_ref::<&str>() {
    128            Some(s)
    129        } else if let Some(s) = self.payload.downcast_ref::<String>() {
    130            Some(s)
    131        } else {
    132            None
    133        }
    134    }
    135
    136    /// Returns information about the location from which the panic originated,
    137    /// if available.
    138    ///
    139    /// This method will currently always return [`Some`], but this may change
    140    /// in future versions.
    141    ///
    142    /// # Examples
    143    ///
    144    /// ```should_panic
    145    /// use std::panic;
    146    ///
    147    /// panic::set_hook(Box::new(|panic_info| {
    148    ///     if let Some(location) = panic_info.location() {
    149    ///         println!("panic occurred in file '{}' at line {}",
    150    ///             location.file(),
    151    ///             location.line(),
    152    ///         );
    153    ///     } else {
    154    ///         println!("panic occurred but can't get location information...");
    155    ///     }
    156    /// }));
    157    ///
    158    /// panic!("Normal panic");
    159    /// ```
    160    #[must_use]
    161    #[inline]
    162    #[stable(feature = "panic_hooks", since = "1.10.0")]
    163    pub fn location(&self) -> Option<&Location<'_>> {
    164        // NOTE: If this is changed to sometimes return None,
    165        // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt.
    166        Some(&self.location)
    167    }
    168
    169    /// Returns whether the panic handler is allowed to unwind the stack from
    170    /// the point where the panic occurred.
    171    ///
    172    /// This is true for most kinds of panics with the exception of panics
    173    /// caused by trying to unwind out of a `Drop` implementation or a function
    174    /// whose ABI does not support unwinding.
    175    ///
    176    /// It is safe for a panic handler to unwind even when this function returns
    177    /// false, however this will simply cause the panic handler to be called
    178    /// again.
    179    #[must_use]
    180    #[inline]
    181    #[unstable(feature = "panic_can_unwind", issue = "92988")]
    182    pub fn can_unwind(&self) -> bool {
    183        self.can_unwind
    184    }
    185
    186    #[unstable(
    187        feature = "panic_internals",
    188        reason = "internal details of the implementation of the `panic!` and related macros",
    189        issue = "none"
    190    )]
    191    #[doc(hidden)]
    192    #[inline]
    193    pub fn force_no_backtrace(&self) -> bool {
    194        self.force_no_backtrace
    195    }
    196}
    197
    198#[stable(feature = "panic_hook_display", since = "1.26.0")]
    199impl fmt::Display for PanicHookInfo<'_> {
    200    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    201        formatter.write_str("panicked at ")?;
    202        self.location.fmt(formatter)?;
    203        if let Some(payload) = self.payload_as_str() {
    204            formatter.write_str(":\n")?;
    205            formatter.write_str(payload)?;
    206        }
    207        Ok(())
    208    }
    209}
    210
    211#[doc(hidden)]
    212#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")]
    213#[allow_internal_unstable(libstd_sys_internals, const_format_args, panic_internals, rt)]
    214#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_2015_macro")]
    215#[rustc_macro_transparency = "semitransparent"]
    216pub macro panic_2015 {
    217    () => ({
    218        $crate::rt::begin_panic("explicit panic")
    219    }),
    220    ($msg:expr $(,)?) => ({
    221        $crate::rt::begin_panic($msg);
    222    }),
    223    // Special-case the single-argument case for const_panic.
    224    ("{}", $arg:expr $(,)?) => ({
    225        $crate::rt::panic_display(&$arg);
    226    }),
    227    ($fmt:expr, $($arg:tt)+) => ({
    228        // Semicolon to prevent temporaries inside the formatting machinery from
    229        // being considered alive in the caller after the panic_fmt call.
    230        $crate::rt::panic_fmt($crate::const_format_args!($fmt, $($arg)+));
    231    }),
    232}
    233
    234#[stable(feature = "panic_hooks", since = "1.10.0")]
    235pub use core::panic::Location;
    236#[doc(hidden)]
    237#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")]
    238pub use core::panic::panic_2021;
    239#[stable(feature = "catch_unwind", since = "1.9.0")]
    240pub use core::panic::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe};
    241
    242#[unstable(feature = "panic_update_hook", issue = "92649")]
    243pub use crate::panicking::update_hook;
    244#[stable(feature = "panic_hooks", since = "1.10.0")]
    245pub use crate::panicking::{set_hook, take_hook};
    246
    247/// Panics the current thread with the given message as the panic payload.
    248///
    249/// The message can be of any (`Any + Send`) type, not just strings.
    250///
    251/// The message is wrapped in a `Box<'static + Any + Send>`, which can be
    252/// accessed later using [`PanicHookInfo::payload`].
    253///
    254/// See the [`panic!`] macro for more information about panicking.
    255#[stable(feature = "panic_any", since = "1.51.0")]
    256#[inline]
    257#[track_caller]
    258#[cfg_attr(not(test), rustc_diagnostic_item = "panic_any")]
    259pub fn panic_any<M: 'static + Any + Send>(msg: M) -> ! {
    260    crate::panicking::begin_panic(msg);
    261}
    262
    263#[stable(feature = "catch_unwind", since = "1.9.0")]
    264impl<T: ?Sized> UnwindSafe for Mutex<T> {}
    265#[stable(feature = "catch_unwind", since = "1.9.0")]
    266impl<T: ?Sized> UnwindSafe for RwLock<T> {}
    267#[stable(feature = "catch_unwind", since = "1.9.0")]
    268impl UnwindSafe for Condvar {}
    269
    270#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
    271impl<T: ?Sized> RefUnwindSafe for Mutex<T> {}
    272#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
    273impl<T: ?Sized> RefUnwindSafe for RwLock<T> {}
    274#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
    275impl RefUnwindSafe for Condvar {}
    276
    277// https://github.com/rust-lang/rust/issues/62301
    278#[stable(feature = "hashbrown", since = "1.36.0")]
    279impl<K, V, S> UnwindSafe for collections::HashMap<K, V, S>
    280where
    281    K: UnwindSafe,
    282    V: UnwindSafe,
    283    S: UnwindSafe,
    284{
    285}
    286
    287#[unstable(feature = "abort_unwind", issue = "130338")]
    288pub use core::panic::abort_unwind;
    289
    290/// Invokes a closure, capturing the cause of an unwinding panic if one occurs.
    291///
    292/// This function will return `Ok` with the closure's result if the closure does
    293/// not panic, and will return `Err(cause)` if the closure panics. The `cause`
    294/// returned is the object with which panic was originally invoked.
    295///
    296/// Rust functions that are expected to be called from foreign code that does
    297/// not support unwinding (such as C compiled with `-fno-exceptions`) should be
    298/// defined using `extern "C"`, which ensures that if the Rust code panics, it
    299/// is automatically caught and the process is aborted. If this is the desired
    300/// behavior, it is not necessary to use `catch_unwind` explicitly. This
    301/// function should instead be used when more graceful error-handling is needed.
    302///
    303/// It is **not** recommended to use this function for a general try/catch
    304/// mechanism. The [`Result`] type is more appropriate to use for functions that
    305/// can fail on a regular basis. Additionally, this function is not guaranteed
    306/// to catch all panics, see the "Notes" section below.
    307///
    308/// The closure provided is required to adhere to the [`UnwindSafe`] trait to
    309/// ensure that all captured variables are safe to cross this boundary. The
    310/// purpose of this bound is to encode the concept of [exception safety][rfc] in
    311/// the type system. Most usage of this function should not need to worry about
    312/// this bound as programs are naturally unwind safe without `unsafe` code. If
    313/// it becomes a problem the [`AssertUnwindSafe`] wrapper struct can be used to
    314/// quickly assert that the usage here is indeed unwind safe.
    315///
    316/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
    317///
    318/// # Notes
    319///
    320/// This function **might not catch all Rust panics**. A Rust panic is not
    321/// always implemented via unwinding, but can be implemented by aborting the
    322/// process as well. This function *only* catches unwinding panics, not those
    323/// that abort the process.
    324///
    325/// If a custom panic hook has been set, it will be invoked before the panic is
    326/// caught, before unwinding.
    327///
    328/// Although unwinding into Rust code with a foreign exception (e.g. an
    329/// exception thrown from C++ code, or a `panic!` in Rust code compiled or
    330/// linked with a different runtime) via an appropriate ABI (e.g. `"C-unwind"`)
    331/// is permitted, catching such an exception using this function will have one
    332/// of two behaviors, and it is unspecified which will occur:
    333///
    334/// * The process aborts, after executing all destructors of `f` and the
    335///   functions it called.
    336/// * The function returns a `Result::Err` containing an opaque type.
    337///
    338/// Finally, be **careful in how you drop the result of this function**. If it
    339/// is `Err`, it contains the panic payload, and dropping that may in turn
    340/// panic!
    341///
    342/// # Examples
    343///
    344/// ```
    345/// use std::panic;
    346///
    347/// let result = panic::catch_unwind(|| {
    348///     println!("hello!");
    349/// });
    350/// assert!(result.is_ok());
    351///
    352/// let result = panic::catch_unwind(|| {
    353///     panic!("oh no!");
    354/// });
    355/// assert!(result.is_err());
    356/// ```
    357#[stable(feature = "catch_unwind", since = "1.9.0")]
    358pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
    359    unsafe { panicking::catch_unwind(f) }
    360}
    361
    362/// Triggers a panic without invoking the panic hook.
    363///
    364/// This is designed to be used in conjunction with [`catch_unwind`] to, for
    365/// example, carry a panic across a layer of C code.
    366///
    367/// # Notes
    368///
    369/// Note that panics in Rust are not always implemented via unwinding, but they
    370/// may be implemented by aborting the process. If this function is called when
    371/// panics are implemented this way then this function will abort the process,
    372/// not trigger an unwind.
    373///
    374/// # Examples
    375///
    376/// ```should_panic
    377/// use std::panic;
    378///
    379/// let result = panic::catch_unwind(|| {
    380///     if 1 != 2 {
    381///         panic!("oh no!");
    382///     }
    383/// });
    384///
    385/// if let Err(err) = result {
    386///     panic::resume_unwind(err);
    387/// }
    388/// ```
    389#[stable(feature = "resume_unwind", since = "1.9.0")]
    390pub fn resume_unwind(payload: Box<dyn Any + Send>) -> ! {
    391    panicking::resume_unwind(payload)
    392}
    393
    394/// Makes all future panics abort directly without running the panic hook or unwinding.
    395///
    396/// There is no way to undo this; the effect lasts until the process exits or
    397/// execs (or the equivalent).
    398///
    399/// # Use after fork
    400///
    401/// This function is particularly useful for calling after `libc::fork`.  After `fork`, in a
    402/// multithreaded program it is (on many platforms) not safe to call the allocator.  It is also
    403/// generally highly undesirable for an unwind to unwind past the `fork`, because that results in
    404/// the unwind propagating to code that was only ever expecting to run in the parent.
    405///
    406/// `panic::always_abort()` helps avoid both of these.  It directly avoids any further unwinding,
    407/// and if there is a panic, the abort will occur without allocating provided that the arguments to
    408/// panic can be formatted without allocating.
    409///
    410/// Examples
    411///
    412/// ```no_run
    413/// #![feature(panic_always_abort)]
    414/// use std::panic;
    415///
    416/// panic::always_abort();
    417///
    418/// let _ = panic::catch_unwind(|| {
    419///     panic!("inside the catch");
    420/// });
    421///
    422/// // We will have aborted already, due to the panic.
    423/// unreachable!();
    424/// ```
    425#[unstable(feature = "panic_always_abort", issue = "84438")]
    426pub fn always_abort() {
    427    crate::panicking::panic_count::set_always_abort();
    428}
    429
    430/// The configuration for whether and how the default panic hook will capture
    431/// and display the backtrace.
    432#[derive(Debug, Copy, Clone, PartialEq, Eq)]
    433#[unstable(feature = "panic_backtrace_config", issue = "93346")]
    434#[non_exhaustive]
    435pub enum BacktraceStyle {
    436    /// Prints a terser backtrace which ideally only contains relevant
    437    /// information.
    438    Short,
    439    /// Prints a backtrace with all possible information.
    440    Full,
    441    /// Disable collecting and displaying backtraces.
    442    Off,
    443}
    444
    445impl BacktraceStyle {
    446    pub(crate) fn full() -> Option<Self> {
    447        if cfg!(feature = "backtrace") { Some(BacktraceStyle::Full) } else { None }
    448    }
    449
    450    fn as_u8(self) -> u8 {
    451        match self {
    452            BacktraceStyle::Short => 1,
    453            BacktraceStyle::Full => 2,
    454            BacktraceStyle::Off => 3,
    455        }
    456    }
    457
    458    fn from_u8(s: u8) -> Option<Self> {
    459        match s {
    460            1 => Some(BacktraceStyle::Short),
    461            2 => Some(BacktraceStyle::Full),
    462            3 => Some(BacktraceStyle::Off),
    463            _ => None,
    464        }
    465    }
    466}
    467
    468// Tracks whether we should/can capture a backtrace, and how we should display
    469// that backtrace.
    470//
    471// Internally stores equivalent of an Option<BacktraceStyle>.
    472static SHOULD_CAPTURE: Atomic<u8> = AtomicU8::new(0);
    473
    474/// Configures whether the default panic hook will capture and display a
    475/// backtrace.
    476///
    477/// The default value for this setting may be set by the `RUST_BACKTRACE`
    478/// environment variable; see the details in [`get_backtrace_style`].
    479#[unstable(feature = "panic_backtrace_config", issue = "93346")]
    480pub fn set_backtrace_style(style: BacktraceStyle) {
    481    if cfg!(feature = "backtrace") {
    482        // If the `backtrace` feature of this crate is enabled, set the backtrace style.
    483        SHOULD_CAPTURE.store(style.as_u8(), Ordering::Relaxed);
    484    }
    485}
    486
    487/// Checks whether the standard library's panic hook will capture and print a
    488/// backtrace.
    489///
    490/// This function will, if a backtrace style has not been set via
    491/// [`set_backtrace_style`], read the environment variable `RUST_BACKTRACE` to
    492/// determine a default value for the backtrace formatting:
    493///
    494/// The first call to `get_backtrace_style` may read the `RUST_BACKTRACE`
    495/// environment variable if `set_backtrace_style` has not been called to
    496/// override the default value. After a call to `set_backtrace_style` or
    497/// `get_backtrace_style`, any changes to `RUST_BACKTRACE` will have no effect.
    498///
    499/// `RUST_BACKTRACE` is read according to these rules:
    500///
    501/// * `0` for `BacktraceStyle::Off`
    502/// * `full` for `BacktraceStyle::Full`
    503/// * `1` for `BacktraceStyle::Short`
    504/// * Other values are currently `BacktraceStyle::Short`, but this may change in
    505///   the future
    506///
    507/// Returns `None` if backtraces aren't currently supported.
    508#[unstable(feature = "panic_backtrace_config", issue = "93346")]
    509pub fn get_backtrace_style() -> Option<BacktraceStyle> {
    510    if !cfg!(feature = "backtrace") {
    511        // If the `backtrace` feature of this crate isn't enabled quickly return
    512        // `Unsupported` so this can be constant propagated all over the place
    513        // to optimize away callers.
    514        return None;
    515    }
    516
    517    let current = SHOULD_CAPTURE.load(Ordering::Relaxed);
    518    if let Some(style) = BacktraceStyle::from_u8(current) {
    519        return Some(style);
    520    }
    521
    522    let format = match crate::env::var_os("RUST_BACKTRACE") {
    523        Some(x) if &x == "0" => BacktraceStyle::Off,
    524        Some(x) if &x == "full" => BacktraceStyle::Full,
    525        Some(_) => BacktraceStyle::Short,
    526        None if crate::sys::FULL_BACKTRACE_DEFAULT => BacktraceStyle::Full,
    527        None => BacktraceStyle::Off,
    528    };
    529
    530    match SHOULD_CAPTURE.compare_exchange(0, format.as_u8(), Ordering::Relaxed, Ordering::Relaxed) {
    531        Ok(_) => Some(format),
    532        Err(new) => BacktraceStyle::from_u8(new),
    533    }
    534}