What is the purpose of function-like procedural macros? Are there any function-like procedural macros in standard library?

In this section of documentation about procedural macros, the docs mention that there are mainly three types of macros, those are:

  • Function-like e.g. custom!()
  • Derive e.g. #[derive(Custom)]
  • Attribute e.g. #[custom]

I can understand attribute and procedural macros, but, at the same time, I wonder why function-like macros exist. Can't we have the same thing with declarative macros using macro_rules!?

So my question is:

  • What is the purpose of function-like macros?
  • Are there any function-like macros defined in the standard library? I think cfg!() is one of them.

Function-like macros can do arbitrary things. Unlike declarative macros.

The most famous ones are format_args! and include_bytes!.

No. For example, declarative macros can't conjure new identifiers.

2 Likes

Strangely, the source code that the docs of format_args! refer to a declarative macro. :thinking:

Same goes for include_bytes! as well.

Those are not real macros (their "definition" is empty), they are compiler built-ins.

Correct me if I'm wrong. I believe that means, for a very simple example, the code below wouldn't compile with declarative macros:

macro_rules! new_struct_creator {
    () => {
        struct NewStruct {
            a: i32,
        }
    }
}

This wouldn't compile (haven't tried it actually) because it conjures a new identifier (creates a new struct)? Is that what you mean?

No. All of the identifiers that macro deals with are spelled out literally in the code, and it compiles. This doesn't create any new identifiers. It declares a new type, but that's uninteresting from the PoV of macros (they have nothing to do with type checking and don't care about types).

Macros are purely syntactic abstractions. When I mean a new identifier, I mean an identifier and nothing more (in particular, I don't mean any program construct such as a type or variable that identifiers may refer to).

2 Likes