Allow panic! macro to take static strings as only argument

I'm making an embedded library and I have multiple parts of my code which panic under similar conditions with the same message. Originally I just used string literals, but in an effort to clean up my code I wanted to use a single static string to be referenced and just panic with that.

The issue is when you panic via panic!("{}", STATIC_MSG);, it adds all the formatting code as well, which is ~2kb in total. This isn't much, but when you're programming for embedded systems with limited memory this can be a big problem.

I propose allowing the panic! macro to take a static string as it's first argument, or, if more desirable, make panicking::panic() a non-nightly function. Having it reference a static string directly would reduce program size and allow for more readable and consistent code.

As a workaround, you could make a macro for your repeated message:

macro_rules! panic_foo {
    () => { panic!("oh, phooey!") }
}

Or even just a function:

fn panic_foo() -> ! {
    panic!("oh, phooey!");
}
1 Like

In other languages, it has proven to be a bad idea to have any ambiguity whatsoever over whether a string is a format string (containing instructions for what to print) or a value (to be printed literally). panic! used to have some of this confusion, and it was removed in the 2021 edition and later.

However, I have an alternative to propose: there are already optimizations for particular cases of format strings, and it seems plausible to me to say that panic!("{}", STATIC_MSG) or panic!("{STATIC_MSG}"), where the format string is exactly one {} and the argument is a string type, should be handled without invoking the general formatting system. This gets the effect you want without changing the meaning of panic!() in general.


As the best available option in current Rust, I would suggest writing a function such as @cuviper’s panic_foo() example. This has another code size advantage — it avoids storing the panic location information for each separate call site, combining them into one location. (And if this is undesirable, you can add #[track_caller] to the function.)

6 Likes

Would panic_any work for this use case? It should be able to accept &'static str just fine. One potential problem is that it seems to be only in std, not core or alloc (this may be partly because it uses a memory allocator – but panic! would also need one if the panic is handled, and neither conceptually needs one if the panic isn't handled).

It's possible that it's only in std because panic_any is primarily intended for panics that are handled, whereas panic! is primarily intended for panics that are unhandled.

There's also core::panicking::panic which is exactly what you want, but nightly-only (and it doesn't link a tracking issue, implying that it may be perma-unstable).

That is not available with no_std and requires a memory allocation.

I'll go with a panic function, my proposal was mostly just because I assumed it would be the easiest to implement, but it looks like the panic! macro used to do that and was specifically designed to not do that in later editions.

It would probably be the easiest and most straightforward to just make a non-nightly version of the nightly panic function, though I also know that features like that can take a while to implement..

Something like core::panic::panic_with_str() perhaps?

The previous version of this post was wrong.

std::panic::panic_any(MY_STATIC_STR) does the same as std::panic!(MY_STATIC_STR) did in editions 2015 and 2018. It does allocate for a place to store the &str but it does not invoke format_args!. But as bjorn3 pointed out this requires std.

On the other hand, core::panic(MY_STATIC_STR) invokes format_args! in all editions where it is allowed.

previous version of this post with correction From the **2015 and 2018** `core::panic` documentation:

core::panic!(x) with a single argument requires that x be &str, but otherwise behaves like std::panic!. In particular, the string need not be a literal, and is not interpreted as a format string.