– [The Rust Reference](https://doc.rust-lang.org/reference/macros-by-example.html)
– [TRPL](https://doc.rust-lang.org/book/ch19-06-macros.html)
– [RBE](https://doc.rust-lang.org/rust-by-example/macros.html)
Copyright By PowCoder代写 加微信 powcoder
– [The Little Book of ](https://veykril.github.io/tlborm/proc-macros/methodical.html)
This week we will talk about macro.
Fundamentally, macros are a way of *writing code that writes other code*, which is known as metaprogramming.
Some frequently used macros are `vec!` and `println!`. All of these macros expand at compile time to produce more code than the code you’ve written manually. Because of this, macro definitions are usually more complicated than function definitions.
# The difference between macros and functions
– common point:
Metaprogramming and function are both useful for reducing the amount of code you have to write and maintain.
– difference:
1. A function signature *must* declare the number and type of parameters the function has. Macros, on the other hand, can take a *variable* number of parameters.
2. macros are *expanded* before the compiler interprets the meaning of the code, so a macro can, for example, implement a trait on a given type. A function can’t, because it gets called at *runtime* and a trait needs to be implemented at compile time.
3. Macro definitions are usually more complex and you have to bring them into scope before you can call them in a file.
There are three kinds of 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
# Declarative Macros for General Metaprogramming
The most widely used form of macros in Rust is `declarative macros`. For detailed usage, please check its [document](https://doc.rust-lang.org/reference/macros-by-example.html).
At their core, declarative macros allow you to write something similar to a Rust `match` expression. In this situation, the value is the literal Rust source code passed to the macro; the patterns are compared with the structure of that source code; and the code associated with each pattern, when matched, replaces the code passed to the macro. This all happens during compilation.
To define a macro, you use the `macro_rules!` construct. Let’s explore how to use `macro_rules!` by looking at how the `vec!` macro is defined.
#![allow(unused)]
fn main() {
let v1: Vec
let v2: Vec
let v3: Vec
Have you ever think about why you can pass any number of parameters you want to `vec!`? This is the magic of macro! A slightly simplified definition of the `vec!` macro is defined as:
#[macro_export] // bring this macro into scope
macro_rules! vec { // similar with fn keyword. {} wraps the macro body.
( $( $x:expr ),* ) => { // pattern to match
let mut temp_vec = Vec::new();
temp_vec.push($x);
The valid pattern syntax in macro is different than `match` because macro patterns are matched against Rust code structure rather than values. In the above example,
– First, a set of parentheses encompasses the whole pattern.
– A dollar sign (`$`) is next, followed by a set of parentheses that captures values that match the pattern within the parentheses for use in the replacement code.
– Within `$()` is `$x:expr`, which matches any Rust expression and gives the expression the name `$x`. Here the `$x` pattern matches three times with the three expressions 1, 2, and 3. `expr`, which represents expression, is one of the designators. Other designators include `ident` (var/fn name), `path`, `tt`(token tree), etc.
– `temp_vec.push()` within `$(),*` is generated for each part that matches `$()` in the pattern zero or more times depending on how many times the pattern matches. `,` is a separator. The `$x` is replaced with each expression matched. Other repeat symbols include `+` (at least one) and `?` (zero or one)
Here the macro expands the code as:
let mut temp_vec = Vec::new();
temp_vec.push(1);
temp_vec.push(2);
temp_vec.push(3);
We’ve defined a macro that can take *any* number of arguments of *any* type and can generate code to create a vector containing the specified elements.
# Procedural macros
Procedural macros accept some *code* as an input, operate on that code, and produce some *code* as an output rather than matching against patterns and replacing the code with other code as declarative macros do.
There are three kinds of procedural macros
– custom derive
– attribute-like
– function-like
But overall, all those three types follow the definition:
use proc_macro;
#[some_attribute] // place holder
pub fn some_name (input: TokenStream) -> TokenStream {
The name “some_attribute” and “some_name” are place holders, and the type `TokenStream` is a type defined in the `proc_macro` crate and represents a sequence of tokens.
This is the core of the macro: the source code that the macro is operating on makes up the input `TokenStream`, and the code the macro produces is the output `TokenStream`.
When creating procedural macros, the definitions *must* reside in their own crate with a special crate type.
## Custom derive Macro
Let’s create a crate named `hello_macro` that defines a trait named `HelloMacro` with one associated function named `hello_macro`.
Rather than making our crate users implement the `HelloMacro` trait for each of their types, we’ll provide a procedural macro so users can annotate their type with `#[derive(HelloMacro)]` to get a default implementation of the `hello_macro` function. The default implementation will print `Hello, Macro! My name is TypeName!` where `TypeName` is the name of the type on which this trait has been defined. In other words, we’ll write a crate that enables another programmer to write code using our crate.
Without procedural macro, we can define a `HelloMacro` trait and then implement this trait for every type:
pub trait HelloMacro {
fn hello_macro();
struct Pancakes;
impl HelloMacro for Pancakes {
fn hello_macro() {
println!(“Hello, Macro! My name is Pancakes!”);
fn main() {
Pancakes::hello_macro();
As a smart coder, we don’t want to do this for every type. Instead, we want something like
Filename: src/main.rs
use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;
#[derive(HelloMacro)]
struct Pancakes;
fn main() {
Pancakes::hello_macro();
Currently, procedural macros need to be in their own crate. Let’s start a new crate called `hello_macro_derive` inside our `hello_macro` project
$ cargo new hello_macro_derive –lib
We need to declare the `hello_macro_derive` crate as a procedural macro crate. We’ll also need functionality from the `syn` and `quote` crates, so we need to add them as dependencies. Add the following to the *Cargo.toml* file for `hello_macro_derive`:
Filename: hello_macro_derive/Cargo.toml
proc-macro = true
[dependencies]
syn = “1.0”
quote = “1.0”
As for the actual definition, almost all procedural macro has the same parsing step, which is the following:
Filename: hello_macro_derive/src/lib.rs
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
// Construct a representation of Rust code as a syntax tree
// that we can manipulate
let ast = syn::parse(input).unwrap();
// Build the trait implementation
impl_hello_macro(&ast)
The code you specify in the body of the inner function (`impl_hello_macro` in this case) will be different depending on your procedural macro’s purpose.
We’ve introduced three new crates:
– `proc_macro`
This crate is the compiler’s API that allows us to read and manipulate Rust code from our code.
This crate parses Rust code from a string into a data structure that we can perform operations on.
turns syn data structures back into Rust code.
The `hello_macro_derive` function will be called when a user of our library specifies `#[derive(HelloMacro)]` on a type. This is possible because we’ve annotated the `hello_macro_derive` function here with `proc_macro_derive` and specified the name, `HelloMacro`, which matches our trait name; this is the convention most procedural macros follow.
The `hello_macro_derive` function first converts the input from a `TokenStream` to a data structure that we can then interpret and perform operations on. This is where `syn` comes into play. The parse function in `syn` takes a `TokenStream` and returns a `DeriveInput` struct representing the parsed Rust code. The relevant parts of the `DeriveInput` struct we get from parsing the `struct Pancakes;` string is:
DeriveInput {
// –snip–
ident: Ident {
ident: “Pancakes”,
span: #0 bytes(95..103)
data: Struct(
DataStruct {
struct_token: Struct,
fields: Unit,
semi_token: Some(
The `DeriveInput` struct is able to describe all sorts of Rust code, but the part we will use is the `ident`, which contains the name of the struct.
Note that the output for our derive macro is also a `TokenStream` and must be a `TokenStream`. The returned `TokenStream` is added to the code that our crate users write, so when they compile their crate, they’ll get the extra functionality that we provide in the modified `TokenStream`.
As for the actual defination of `impl_hello_macro`, it takes the parsed `DeriveInput` as the input and returns a `TokenStream`.
Filename: hello_macro_derive/src/lib.rs
fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let gen = quote! {
impl HelloMacro for #name {
fn hello_macro() {
println!(“Hello, Macro! My name is {}!”, stringify!(#name));
gen.into()
The variable `name` contains the name of the struct as we described above, which can be printed as “pancakes”.
The `quote!` macro lets us define the Rust code that we want to return. The compiler expects something different to the direct result of the `quote!` macro’s execution, so we need to convert it to a `TokenStream`. We do this by calling the `into` method, which consumes this intermediate representation and returns a value of the required `TokenStream` type. The `quote!` macro also provides some very cool templating mechanics: we can enter `#name`, and `quote!` will replace it with the value in the variable name. You can even do some repetition similar to the way regular macros work. Check out [the `quote` crate’s docs](https://docs.rs/quote) for a thorough introduction.
The `stringify!` macro used here is built into Rust. It takes a Rust expression, such as `1 + 2`, and at compile time turns the expression into a string literal, such as `”1 + 2″`. This is different than `format!` or `println!`, macros which *evaluate* the expression and then turn the result into a `String`, so if you give them `1 + 2` they will allocate a string `3`.
Now, let’s see how to use our procedural macro.
We create a new binary project named `pancakes` and add the two crates as the dependencises in the `pancakes` crate’s *Cargo.toml*
hello_macro = { path = “../hello_macro” }
hello_macro_derive = { path = “../hello_macro/hello_macro_derive” }
### `#[derive()]`
Rust also provides many derivable traits for us.
I will list some:
– The `Debug` trait enables debug formatting in format strings, which you indicate by adding 😕 within {} placeholders.
– `PartialEq` and `Eq` for Equality Comparisons
– `PartialOrd` and `Ord` for Ordering Comparisons
– `Clone` and `Copy` for Duplicating Values
– `Hash` for Mapping a Value to a Value of Fixed Size
– `Default` for Default Values
## Attribute-like macros
Attribute-like macros are similar to custom derive macros, but instead of generating code for the `derive` attribute, they allow you to create new attributes. Attributes can be applied to structs, enums and functions, etc.
Say we have an attribute `route` that annotates functions when using a we application framework
#[route(GET, “/”)]
fn index() {
This `#[route]` attribute would be defined by the framework as a procedural macro. The signature of the macro definition function would look like this:
#[proc_macro_attribute]
pub fn route(attr: TokenStream, // GET,”/” part
item: TokenStream // fn index() {}
) -> TokenStream {
You create a crate with the `proc-macro` crate type and implement a function that generates the code you want!
## Function-like macros
Function-like macros are similarly to `macro_rules!` macros, but are more flexible.
Function-like macros take a `TokenStream` parameter and their definition manipulates that `TokenStream` using Rust code as the other two types of procedural macros do. An example of a function-like macro is an `sql!` macro that might be called like so:
let sql = sql!(SELECT * FROM posts WHERE id=1);
This macro would parse the SQL statement inside it and check that it’s syntactically correct, which is much more complex processing than a `macro_rules!` macro can do. The `sql!` macro would be defined like this:
#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
This definition is similar to the custom derive macro’s signature: we receive the tokens that are inside the parentheses and return the code we wanted to generate.
程序代写 CS代考 加微信: powcoder QQ: 1823890830 Email: powcoder@163.com