Rust Metaprogramming with Macros

RustRustBeginner
Practice Now

This tutorial is from open-source community. Access the source code

Introduction

In this lab, we will explore the powerful macro system provided by Rust, which allows for metaprogramming by expanding macros into abstract syntax trees. The macro_rules! macro is used to create macros, and they are distinguished from functions by their ending bang !. Macros are useful for avoiding code repetition, creating domain-specific languages, and defining variadic interfaces for functions that can take a variable number of arguments.

Note: If the lab does not specify a file name, you can use any file name you want. For example, you can use main.rs, compile and run it with rustc main.rs && ./main.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL rust(("`Rust`")) -.-> rust/FunctionsandClosuresGroup(["`Functions and Closures`"]) rust/FunctionsandClosuresGroup -.-> rust/function_syntax("`Function Syntax`") rust/FunctionsandClosuresGroup -.-> rust/expressions_statements("`Expressions and Statements`") subgraph Lab Skills rust/function_syntax -.-> lab-99223{{"`Rust Metaprogramming with Macros`"}} rust/expressions_statements -.-> lab-99223{{"`Rust Metaprogramming with Macros`"}} end

macro_rules!

Rust provides a powerful macro system that allows metaprogramming. As you've seen in previous chapters, macros look like functions, except that their name ends with a bang !, but instead of generating a function call, macros are expanded into source code that gets compiled with the rest of the program. However, unlike macros in C and other languages, Rust macros are expanded into abstract syntax trees, rather than string preprocessing, so you don't get unexpected precedence bugs.

Macros are created using the macro_rules! macro.

// This is a simple macro named `say_hello`.
macro_rules! say_hello {
    // `()` indicates that the macro takes no argument.
    () => {
        // The macro will expand into the contents of this block.
        println!("Hello!")
    };
}

fn main() {
    // This call will expand into `println!("Hello")`
    say_hello!()
}

So why are macros useful?

  1. Don't repeat yourself. There are many cases where you may need similar functionality in multiple places but with different types. Often, writing a macro is a useful way to avoid repeating code. (More on this later)

  2. Domain-specific languages. Macros allow you to define special syntax for a specific purpose. (More on this later)

  3. Variadic interfaces. Sometimes you want to define an interface that takes a variable number of arguments. An example is println! which could take any number of arguments, depending on the format string. (More on this later)

Summary

Congratulations! You have completed the Macro_rules! lab. You can practice more labs in LabEx to improve your skills.

Other Rust Tutorials you may like