Macro with format!() as input argument

Hi all,

I'm trying to get the following macros working. They work fine unless I throw in a format! macro instead of a string literal.

macro_rules! exit {
    ($msg:expr) => {
        println!($msg);
        std::process::exit(1);
    };
}

macro_rules! none_exit {
    ($fcall:expr, $msg:expr) => {
        match $fcall {
            Some(r) => r,
            None => {
                exit!($msg);
            }
        }
    };
}

fn main() {

    // Works
    none_exit!(None, "Something terrible has happened ...");

    // Fails
    none_exit!(None, format!("Something terrible has happened in module {} ...", "X"));
}

I tried quite a few things but I can't get around this error message.

error: format argument must be a string literal
  --> src/main.rs:25:22
   |
25 |     none_exit!(None, format!("Something terrible has happened in module {} ...", "X"));
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
help: you might be missing a string literal to format with
   |
25 |     none_exit!(None, "{}", format!("Something terrible has happened in module {} ...", "X"));
   |                      +++++

Any ideas how to solve this?

Thank you in advance.

Try this:

macro_rules! exit {
    ($($msg:tt)*) => {
        println!($($msg)*);
        std::process::exit(1);
    };
}

macro_rules! none_exit {
    ($fcall:expr, $($msg:tt)*) => {
        match $fcall {
            Some(r) => r,
            None => {
                exit!($($msg)*);
            }
        }
    };
}

fn main() {
    none_exit!(None, "Something terrible has happened in module {} ...", "X");
}
1 Like

It's trying to use format! as a formatting string. You shouldn't but the macro argument directly as the first argument to println!.

But a better approach would be to use the format_args macro..

Or just use expect.

@alice: That's a nice solution. Would you mind explaining a bit what is actually happening here?

The tt in $($msg:tt)* means "token tree", which matches any individual token. Then I use $()* to match any number of tokens. And then I just put those tokens into the println! call.