Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Error reporting: panic vs compile_error!

Every macro you’ve written so far reaches for panic! when the input is wrong — deriving on an enum, a tuple struct, and so on. That’s fine while you’re prototyping, but it’s a poor experience for the people using your macro. This section is the first of four on doing better.

What the user sees when you panic

When a proc macro panics, the compiler catches the panic and turns it into an error like this:

error: proc-macro derive panicked
 --> src/main.rs:3:10
  |
3 | #[derive(Getters)]
  |          ^^^^^^^
  |
  = help: message: Getters can only be derived for structs with named fields

The message is there, but it’s buried under “proc-macro derive panicked,” it’s tagged as a help note, and the span points only at the derive name. It reads like your macro crashed — because, technically, it did.

compile_error! — a real diagnostic

compile_error! is a built-in macro that emits a compiler error with your message when it’s expanded. Instead of panicking, your macro can return a token stream that invokes it:

#![allow(unused)]
fn main() {
return quote! {
    compile_error!("Getters can only be derived for structs with named fields");
};
}

Now the user sees a clean, top-level error:

error: Getters can only be derived for structs with named fields
 --> src/main.rs:3:10
  |
3 | #[derive(Getters)]
  |          ^^^^^^^

No “panicked,” no “help: message:” — just an error, exactly like one the compiler itself would produce. You can even emit a compile_error! alongside valid code, so the rest of your expansion still type-checks and you avoid a cascade of follow-on errors. (More on that idea when we cover attribute macros.)

Why not stop here?

compile_error! is a big step up from a panic, but building the message by hand with quote! gets awkward as soon as you have more than one error site, and the span still points at the whole derive rather than at the specific thing that’s wrong. The next two sections introduce syn::Error, which packages a message together with a span and converts to a compile_error! for you — and lets you point the error exactly where it belongs.

Exercise

The exercise for this section is located in 04_derive/05_panic_vs_compile_error

The Getters derive macro currently panic!s when used on anything other than a named struct. Replace both panics with returned compile_error! invocations. The unit tests in the macro crate check that misuse produces a compile_error! rather than a panic.