I am very confused by the many terms used for several macro-like things in the Rust ecosystem. Could someone clarify what macros/syntax extensions/compiler plugins there are as well as explain the relationship between those terms?
You are right: it is confusing. Especially, because most of those features are unstable and change fairly often. But I'll try to summarize the current situation (December 2016).
Let's start with the Syntax Extension: it's something that has to be "called" or annotated manually in order to have any effect. There are three kinds of syntax extensions, which differ in the way you annotate them:
function-like syntax extensions: these are probably the most common syntax extensions, also called "macros". The syntax for invoking them is foo!(…)
or (and this is pretty rare) foo! some_ident (…)
, where foo
is the macro's name. Note that the ()
parenthesis can be replaced by []
or {}
. Function-like syntax extensions can be defined either as a "macro by example" or as a "procedural macro".
attribute-like syntax extensions: these are invoked like #[foo(…)]
where the parenthesis are not necessary and, again, foo
is the name of the syntax extension. The item the attribute is belonging to can then be modified or extended by additional items (decorator).
custom derives: most Rust-programmers have already used the #[derive(…)]
attribute. Of course, derive
itself can be seen as attribute-like syntax extension. But it can also be extended, which is then invoked like #[derive(Foo)]
, where Foo
is the name of the custom derive.
Most of these syntax extensions are also "compiler plugins". The only exception are function-like syntax extensions which are defined via "macro by example" (meaning macro_rules!
syntax). Macros by example can be defined in your source code without writing a compiler plugin whatsoever.
But there are also compiler plugins that aren't syntax extensions. Those types of compiler plugins are linters or other plugins which run some code at some stage of the compiling process. They don't need to be invoked manually: once loaded, the compiler will call them at certain points during compilation.
All compiler plugins need to be loaded – either via #![plugin(foo)]
at the crate-root or with the -Zextra-plugins=too,bar
command line parameter – before they can have any effect!
Compiler plugins are currently unstable, therefore you need a nightly-compiler to use them. But the "Macro 1.1"-RFC will probably be stabilized soon, which means that a small subsets of compiler plugins can then be used with the stable compiler.
Useful links: