What's the best way to write a custom format! macro?

Viewed 260

I'm trying to create a custom crash! macro that essentially just wraps eprintln!, but it could be any other format!-like macro. It currently looks like this.

macro_rules! crash {
    ($fmt_str:literal, $($args:expr),*) => {{
        eprintln!($fmt_str, $args);
        std::process::exit(1);
    }};
}

I can't use $args directly as it's still repeating, so I need some way to break it apart, but I'm not sure how to do that.

2 Answers

While your solution work, the best way to forward macro arguments is to capture them as tt. In your case, since all you want is to forward them, you can use $($t:tt)*:

macro_rules! crash {
    ($($t:tt)*) => {{
        eprintln!($($t)*);
        std::process::exit(1);
    }};
}

This has the advantage that it is completely transparent and enables everything the underlying macro enables. For instance, in this case, the tt version supports early-expanded format string, while your original version does not (playground):

// crash!(concat!("a", "b")); // Does not compile
crash_tt!(concat!("a", "b"));

Turns out I was just having a mental blank, and ended up answering my own question. I just needed to expand $args... Just for anyone else who ends up here, the solution looks like this:

macro_rules! crash {
    ($fmt_str:literal) => {{
        eprintln!($fmt_str);
        std::process::exit(1);
    }};

    ($fmt_str:literal, $($args:expr),*) => {{
        eprintln!($fmt_str, $($args),*);
        std::process::exit(1);
    }};
}
Related