Categorizing macros

This is what I think I understand about Rust macros:

  • Macros generate code at compile-time.
  • There are two categories of macros, "declarative" and "procedural".
  • There are three kinds of procedural macros: attribute, derive, and function.

Are there any issues with the above statements?

I think I understand the differences between the three kinds of procedural macros, but I don't understand the distinction between declarative and procedural macros. For example, I think vec is a procedural function macro, but maybe it is also a declarative macro.

I've done lots of googling to find a good description of these terms, but so far haven't found one that clarifies this for me.

In terms of determining whether a macro is procedural or not, here are a few clues:

  • Is it used in a #[derive] attribute? Then it's a proc macro.
  • Is it an attribute macro #[SomeMacro]? Then it's a proc macro.

Otherwise, functional and declarative macros are intended to be identical in the API they expose. However, if you're really interested in determining whether they're proc or decl, try the following:

  • Does the macro live in a standalone crate? If so, then it's most likely a proc macro since they require a special crate type for proc macros.
  • Just look at the source for it:
    // Declarative macro:
    macro_rules! foo {
        //
    }
    // Macros 2.0 declarative macro:
    macro foo() {
        //
    }
    // Proc macro:
    #[proc_macro]
    fn foo(input: TokenStream) -> TokenStream {
        //
    }
    
1 Like

Not sure why you think it's a procedural macro. Procedural macros are essentially compiler plugins that can run arbitrary Rust code (hence "procedural") and have access to their input in a partially pre-parsed format. They are much more powerful than declarative macros (macro_rules! a.k.a. "by-example"), but take a significantly bigger effort to write.

In contrast, the vec![] macro is a plain old declarative macro, defined here, nothing more.

I started my exploration here: Macros - The Rust Programming Language which says the following:


The term macro refers to a family of features in Rust: declarative macros with macro_rules! and three kinds of procedural macros:

  • Custom #[derive] macros that specify code added with the derive attribute used on structs and enums
  • Attribute-like macros that define custom attributes usable on any item
  • Function-like macros that look like function calls but operate on the tokens specified as their argument

I now understand what derive macros and attribute-like macros are. But I'm still unclear about the difference between declarative and function-like macros. You gave the example of vec! being a declarative macro, but it also seems like a function-like macro. Are they defined in different ways or have different limitations?

I imagine it's hard to say, but does it seem like the macros 2.0 syntax will be in a stable release soon?

There are three different syntaxes for calling a macro:

  1. Derive
  2. Attribute
  3. Function-like

There are two different ways to define a macro:

  1. Procedural
  2. Declarative

You can use procedural macros to create derives, attributes, or function-like macros. However, the declarative syntax can only create function-like macros.

8 Likes

That's true. It's a function-like macro because you call it similarly to a function call.

However, "function-like" and "procedural" are not synonymous – again, "procedural" means that it's not declared using macro_rules! but as a separate crate that the compiler runs while compiling the code which uses the procedural macro.

1 Like

It's a declarative macro.

The definition is roughly equivalent to this:

macro_rules! vec {
  () => { Vec::new() };
  ($elem:expr ; $n:expr) => { Vec::from_elem($elem, $n) };
  ($($x:expr),+ $(,)?) => { Vec::from(box [ $($x),* ]) };
}
2 Likes

I imagine it's hard to say, but does it seem like the macros 2.0 syntax will be in a stable release soon?

The tracking issue for macros 2.0 is here. At a first glance, it looks like the design isn't quite done yet, so I expect it'll be a while. In particular the RFC still calls itself a "placeholder" and has little in the way of detail. The entire "Detailed Design" section reads:

There will be a new system of declarative macros using similar syntax and semantics to the current macro_rules! system.

A declarative macro is declared using the macro keyword. For example, where a macro foo is declared today as macro_rules! foo { ... } , it will be declared using macro foo { ... } . I leave the syntax of the macro body for later specification.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.