Macros in Rust language

Hello, I'm new to Rust.
I was reading about the macros of Rust but didn't quite get it. I know C and familiar with its marcos.
Although I didn't quite understand Rust's macros yet. Could you answer my basic questions about macros:

  1. What is the difference between functions and macros in Rust?
  2. Why or when Should I use them?
  3. How they are different for C's macros?

Also could you kindly add an examples in order to understand it better?

Functions have specific types of arguments (like u32, String) and are called with normal function-like syntax. Macros don't understand types (apart from general patterns for "identifier" or "expression"), and accept a pattern of arbitrary syntax as their arguments. You can make macro!(a => (b) : c @ d) with custom syntax that doesn't make sense in Rust, but makes sense in the macro.

Functions are namespaced and live in their module (e.g std::fs::create_dir()). Macros are global (panic!() works everywhere).

Rust macros are not text find'n'replace like in C. They are "hygienic" meaning they can't use variables from outside the macro, unless the variable is explicitly passed in to the macro. Names declared inside the macro don't conflict with names outside. They're almost like a function scope. They also have to obey basic syntax rules, for example you can't output incomplete item like "impl Foo {" without the closing "}".

Macros are used to reduce repetition in cases that Rust can't handle with generic functions. More advanced macros are used to generate code at compile time, e.g. for custom #[derive()].

5 Likes

Also, I think functions still need to have a fixed number of parameters, which you can work around with macros. A Std example would be println!. You can give it as many parameters as you want to print them. To my knowledge this is still not possible for functions for good reasons and will probably not change.

1 Like

Spiritually, this is a good overview, but I have two nits to pick:

This is not true of macros in general. panic works everywhere because it is in std, which is special. Macros you define are only visible inside the module where they are defined, unless the module is declared with the #[macro_use] attribute. Example

(In order to be imported by another crate with #[macro_use], macros also have to be defined with the #[macro_export] attribute.)

This overstates things a little. A macro can use local variables as long as they are in scope at the point where the macro is defined. But a macro can't use a variable that will be defined later, nor define a variable that will be used later outside the macro (unless the variable name is passed in as a parameter, as you note).

Side note: Macros defined inside functions can't be exported with #[macro_use], which is only meaningful on modules.

1 Like

kornel didn't tell that macros are automatically imported from the crates you use, just that macros are in the global namespace.

In your example, you wrote hi!() and not foo::hi!().

I haven't looked at them very thoroughly, but it's worth a note that procedural macros seem to be namespaced just like functions, and unlike macro_rules! macros.

AFAIK there are plans for "macros 2.0" to be namespaced and imported like functions (and I guess proc_macros use the 2.0 behavior already).