Panic!("{}") shouldn't compile


#1

I thought I’d post this here in hopes of getting more discussion than I’ve seen on issue #22932.

Problem

The panic! macro is currently overloaded with a single-argument version that accepts Any and a multiple-argument version that accepts the same arguments as format_args!. This has a few consequences:

  • panic!("{}") compiles, but it looks like a broken format.
  • assert!(some_boolean_expression, true) also compiles, but it’s not doing the same thing as assert_eq!. This actually happened in the Rust repository. I suspect this kind of typo can’t do that much harm.
  • panic!("message") is slightly more bloated than it could be. libcore has a single-argument panic! that roughly expands to panicking::panic(&("message", file!(), line!()). The libstd version is roughly rt::begin_unwind("message", &(file!(), line!()). On x86_64, I measured the overhead as 52 bytes for libcore vs 67 bytes for libstd.
  • panic!("x") and panic!("x{}", 42) pass different types of values to JoinHandle::join()&str and String, respectively.

The vast majority of panic! calls I see in the Rust repository are passing a string literal or formatting arguments.

Proposal

Given that, I propose that we split panic! into two macros:

  • panic_any! which accepts a single argument of Any type.
  • panic! which only accepts formatting arguments.

To keep the code size of panic! minimal, we add a new macro, format_args_literal!. format_args_literal! expands to a string literal rather than an Arguments value, and it only accepts a single string literal argument. (Equivalently, it accepts exactly the same inputs as format_args!, but only single-argument formats can be converted to a literal.) format_args_literal! could also be used to optimize println! et al.

Multiple-argument assert! would always take formatting arguments. If any existing assert! calls are passing non-string panic values, they would need to be rewritten using panic_any!.

I’m not sure what type panic!("message") would pass to JoinHandle::join(). If the overloaded single-argument panic! were strictly a call site size optimization, then perhaps it should pass the type String for consistency.

This is a breaking change. It might be too late for 1.0. It seems hard to fix later.

Alternatives

  1. We could add a panic_fmt! macro that accepts formatting arguments. We could use the same format_args_literal! optimization with it, making it OK to use almost everywhere that panic! is used today. I’m unfavorable towards this alternative, because almost all panic! calls use formatting arguments. Passing an Any value seems to be the exception.