Macros

Macro Syntax

Rust by example - macros

Macros are a way of writing code that writes other code, known as metaprogramming. They allow you to define reusable code snippets that can be expanded into more complex code at compile time. Macros are more flexible than functions because they can handle a variable number of arguments and can manipulate code structure directly.

Macros By Example

The macro_rules! macro is used to define a macro by example. The syntax consists of a name for the macro (e.g., create_function), followed by a set of rules enclosed in curly braces {}. Each rule has a pattern and a corresponding code block that will be substituted when the macro is invoked.

In the following example, the pattern ($func_name:ident) matches an identifier (like a function name), and the code block defines a function with that name. The $func_name is a macro variable that will be replaced with the actual identifier provided when the macro is called.

Macro Uses

The following example demonstrates various ways macros can be used in Rust.


use std::cell::RefCell;

fn main() {
    // Macros can be used as expressions.
    // The following defines a vector:
    let _x = vec![1, 2, 3];

    // Macros can be used as statements.
    // Print the string:
    println!("Hello!");

    /// Simple macro by example definition.
    /// This macro wraps an identifier in `Some()`.
    macro_rules! pat {
        ($i:ident) => {
            Some($i)
        };
    }

    // Macros can be used in a pattern.
    // Destructure an `Option`:
    if let pat!(x) = Some(1) {
        assert_eq!(x, 1);
    }

    print_tuple((1, 3));
    use_thread_local();
}

/// This macro by example defines a tuple type.
macro_rules! Tuple {
    { $A:ty, $B:ty } => { ($A, $B) };
}

// Macros can be used in a type alias.
// Define a tuple type:
type T2 = Tuple!(i32, i32);

/// Print the elements of the tuple.
fn print_tuple(tupl: T2) {
    println!("{} {}", tupl.0, tupl.1);
}

/// Macros can be used in a declaration.
fn use_thread_local() {
    thread_local!(static FOO: RefCell<u32> = const { RefCell::new(1) });
    // `thread_local!` declares a new thread local storage key.
}

/// This macro creates a constant of a given type and value.
macro_rules! const_maker {
    ($t:ty, $v:tt) => {
        const CONST: $t = $v;
    };
}

/// Macros can be used as an associated item (here, a `const`).
#[allow(dead_code)]
trait T {
    const_maker! {i32, 7}
}

// It is possible to call macros within a macro.
//
// When used, the outer macro `example` is expanded,
// then the inner macro `println` is expanded.
/// This macro calls the `println!` macro.
macro_rules! _example {
    () => {
        println!("Macro call in a macro!")
    };
}

References

Related Topics

  • Development Tools: Procedural Macro Helpers.
    • Compile Macros.
    • Macro Tools.
    • Write Proc Macros.
  • Rust Patterns.