DEV Community

Sivakumar
Sivakumar

Posted on

Rust Macros

In this blog post, let us explore more in detail how to work with Macros in Rust

What are Macros?

Macros are a way of writing code that writes other code, which is known as metaprogramming

Metaprogramming is useful for reducing the amount of code you write and maintain, which is also one of the roles of functions. However, macros have some additional powers that functions don’t.

Types of Macros

There are two kinds of macros: Declarative and Procedural

Declarative Macros

It is a most used form of macros in Rust. Also referred to as “macros by example”, “macro_rules! macros”.  Allows you to write something similar to match expression

To define a macro, we use macro_rules! Construct. We use #[macro_export] annotation to indicate that this macro should be made available whenever the crate in which the macro is defined is brought into scope.

Declarative Macros Syntax
#[macro_export]
macro_rules! <name of macro> {
    ( $( $x:expr ),* ) => {
        {
            <implementation>
        }
    };
}
Enter fullscreen mode Exit fullscreen mode

Procedural Macros

It acts more like a function (and is a type of procedural)

Accepts some code as an input, operates on that code, and produces some code as an output rather than matching against patterns and replacing the code with other code as declarative macros do.

Three kinds of procedural macros:

Derive

Derive macros define new inputs for the derive attribute. These macros can create new items given the token stream of a struct, enum, or union. They can also derive macro helper attributes.

Derive Procedural Macros Syntax
extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro_derive(<Name of Derive Macro>)]
pub fn derive_macro(_item: TokenStream) -> TokenStream {
    <Implementation>
}
Enter fullscreen mode Exit fullscreen mode
Attribute-like

Attribute macros define new outer attributes which can be attached to items, including items in extern blocks, inherent and trait implementations, and trait definitions.

Attribute Procedural Macros Syntax
extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro_attribute] 
pub fn <Attribute Name>(_attr: TokenStream, item: TokenStream) -> TokenStream { 
<Implementation> 
}
Enter fullscreen mode Exit fullscreen mode
Function-like

Function-like procedural macros are procedural macros that are invoked using the macro invocation operator (!)

Function-like Procedural Macros Syntax
extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro]
pub fn <Macro Name>(_item: TokenStream) -> TokenStream {
    <Implementation>
}
Enter fullscreen mode Exit fullscreen mode

Macros vs Functions

Macros are more complex to implement than function definitions because we’re writing Rust code that writes Rust code.

Macro definitions are generally more difficult to read, understand and maintain than function definitions

Macros must define first or bring them into scope before you call in a file, as opposed to functions you can define anywhere and call anywhere

Please feel free to share your comments if any

Happy reading!!!

Top comments (0)