Working on Rust? Must know about Macros

Reading Time: 3 minutes

A programmable pattern which translates a certain sequence of input into a preset sequence of output is known as Macros( which stands for macroinstructions). Macros can make tasks less repetitive by representing a complicated sequence of keystrokes, mouse movements, commands, or other types of input.

Rust macros

In Rust, We’ve used macros like println! , but we haven’t fully explored what a macro is and how it works. 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

Declarative Macros with macro_rules! for General Metaprogramming

Declarative macros is the most widely used form of macros in Rust. These are also sometimes referred to as “macros by example,” “macro_rules! macros,” or just plain “macros.” At their core, declarative macros allow you to write something similar to a Rust match expression.

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. For example, the following macro creates a new vector containing three integers:

fn main() {
let v: Vec<u32> = vec![1, 2, 3];

We could also use the vec! macro to make a vector of two integers or a vector of five string slices. We wouldn’t be able to use a function to do the same because we wouldn’t know the number or type of values up front.

Procedural Macros for Generating Code from Attributes

Procedural macros are more like functions and are a type of procedure. They 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.

The three kinds of procedural macros (custom derive, attribute-like, and function-like) all work in a similar fashion.

When creating procedural macros, the definitions must be in their own crate with a special crate type. This is for complex technical reasons that we hope to eliminate in the future. Using procedural macros looks like the code in given below, where some_attribute is a placeholder for using a specific macro.

use proc_macro;

pub fn some_name(input: TokenStream) -> TokenStream {

The function that defines a procedural macro takes a TokenStream as an input and produces a TokenStream as an output. The TokenStream type is defined by the proc_macro crate that is included with Rust 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. The function also has an attribute attached to it that specifies which kind of procedural macro we’re creating. We can have multiple kinds of procedural macros in the same crate.

How to write a custom derive macro?

You can create a custom derive macro using following procedure:

  1. Create a crate for your procedural macros:
cargo new my_derive --lib

2. Edit the Cargo.toml to make it a procedural macro crate:

proc-macro = true

3. Implement your procedural macro:

extern crate proc_macro;

use proc_macro::TokenStream;

pub fn my_macro_here_derive(input: TokenStream) -> TokenStream { 
    // ...

4. Import the procedural macro and use it:

extern crate my_derive;

use my_derive::MyMacroHere;

struct Example {
    id: i64,
    value: Option<String>,

So this was about Macros in Rust. I hope this will help you all. Stay connected to explore more such topics. Thank you for reading!!

If you want to read more content like this?  Subscribe to Rust Times Newsletter and receive insights and latest updates, bi-weekly, straight into your inbox. Subscribe to Rust Times Newsletter:


Written by 

Ayushi is a Software Developer having more than 1.5 year of experience in RUST. Her practice area is Rust and Go. She loves to solve daily coding challenges.