diff --git a/core/src/panic.rs b/core/src/panic.rs index b5a0932221ad6..56ede02673c03 100644 --- a/core/src/panic.rs +++ b/core/src/panic.rs @@ -12,6 +12,8 @@ use crate::any::Any; pub use self::location::Location; #[stable(feature = "panic_hooks", since = "1.10.0")] pub use self::panic_info::PanicInfo; +#[unstable(feature = "panic_info_message", issue = "66745")] +pub use self::panic_info::PanicMessage; #[stable(feature = "catch_unwind", since = "1.9.0")] pub use self::unwind_safe::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe}; diff --git a/core/src/panic/panic_info.rs b/core/src/panic/panic_info.rs index 78cf1d2e98eca..91953fd656b6a 100644 --- a/core/src/panic/panic_info.rs +++ b/core/src/panic/panic_info.rs @@ -1,4 +1,4 @@ -use crate::fmt; +use crate::fmt::{self, Display}; use crate::panic::Location; /// A struct providing information about a panic. @@ -18,6 +18,17 @@ pub struct PanicInfo<'a> { force_no_backtrace: bool, } +/// A message that was given to the `panic!()` macro. +/// +/// The [`Display`] implementation of this type will format the message with the arguments +/// that were given to the `panic!()` macro. +/// +/// See [`PanicInfo::message`]. +#[unstable(feature = "panic_info_message", issue = "66745")] +pub struct PanicMessage<'a> { + message: fmt::Arguments<'a>, +} + impl<'a> PanicInfo<'a> { #[inline] pub(crate) fn new( @@ -29,12 +40,26 @@ impl<'a> PanicInfo<'a> { PanicInfo { location, message, can_unwind, force_no_backtrace } } - /// The message that was given to the `panic!` macro, - /// ready to be formatted with e.g. [`fmt::write`]. + /// The message that was given to the `panic!` macro. + /// + /// # Example + /// + /// The type returned by this method implements `Display`, so it can + /// be passed directly to [`write!()`] and similar macros. + /// + /// [`write!()`]: core::write + /// + /// ```ignore (no_std) + /// #[panic_handler] + /// fn panic_handler(panic_info: &PanicInfo<'_>) -> ! { + /// write!(DEBUG_OUTPUT, "panicked: {}", panic_info.message()); + /// loop {} + /// } + /// ``` #[must_use] #[unstable(feature = "panic_info_message", issue = "66745")] - pub fn message(&self) -> fmt::Arguments<'_> { - self.message + pub fn message(&self) -> PanicMessage<'_> { + PanicMessage { message: self.message } } /// Returns information about the location from which the panic originated, @@ -116,7 +141,7 @@ impl<'a> PanicInfo<'a> { } #[stable(feature = "panic_hook_display", since = "1.26.0")] -impl fmt::Display for PanicInfo<'_> { +impl Display for PanicInfo<'_> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("panicked at ")?; self.location.fmt(formatter)?; @@ -125,3 +150,41 @@ impl fmt::Display for PanicInfo<'_> { Ok(()) } } + +impl<'a> PanicMessage<'a> { + /// Get the formatted message, if it has no arguments to be formatted at runtime. + /// + /// This can be used to avoid allocations in some cases. + /// + /// # Guarantees + /// + /// For `panic!("just a literal")`, this function is guaranteed to + /// return `Some("just a literal")`. + /// + /// For most cases with placeholders, this function will return `None`. + /// + /// See [`fmt::Arguments::as_str`] for details. + #[unstable(feature = "panic_info_message", issue = "66745")] + #[rustc_const_unstable(feature = "const_arguments_as_str", issue = "103900")] + #[must_use] + #[inline] + pub const fn as_str(&self) -> Option<&'static str> { + self.message.as_str() + } +} + +#[unstable(feature = "panic_info_message", issue = "66745")] +impl Display for PanicMessage<'_> { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_fmt(self.message) + } +} + +#[unstable(feature = "panic_info_message", issue = "66745")] +impl fmt::Debug for PanicMessage<'_> { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_fmt(self.message) + } +} diff --git a/std/src/panicking.rs b/std/src/panicking.rs index 2bb5ea28b181c..ebd054156951d 100644 --- a/std/src/panicking.rs +++ b/std/src/panicking.rs @@ -593,19 +593,18 @@ pub fn panicking() -> bool { #[panic_handler] pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { struct FormatStringPayload<'a> { - inner: &'a fmt::Arguments<'a>, + inner: &'a core::panic::PanicMessage<'a>, string: Option, } impl FormatStringPayload<'_> { fn fill(&mut self) -> &mut String { - use crate::fmt::Write; - let inner = self.inner; // Lazily, the first time this gets called, run the actual string formatting. self.string.get_or_insert_with(|| { let mut s = String::new(); - let _err = s.write_fmt(*inner); + let mut fmt = fmt::Formatter::new(&mut s); + let _err = fmt::Display::fmt(&inner, &mut fmt); s }) } @@ -627,7 +626,11 @@ pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { impl fmt::Display for FormatStringPayload<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(s) = &self.string { f.write_str(s) } else { f.write_fmt(*self.inner) } + if let Some(s) = &self.string { + f.write_str(s) + } else { + fmt::Display::fmt(&self.inner, f) + } } }