Advanced Macro Usage in Rust

Multiple Examples and Best Practices

Sterling Cobb
3 min readJan 7, 2023

Macros in Rust are a powerful tool that allow developers to write code that generates other code at compile time. They can be used to abstract away repetitive or boilerplate code, as well as to create custom language constructs. This article will cover advanced macro usage in Rust, including examples and best practices.

Examples

One common use case for macros is to create custom assert_* functions for unit testing. For example, the following macro creates an assert_approx_eq function that compares two floating point numbers for approximate equality:

#[macro_export]
macro_rules! assert_approx_eq {
($x:expr, $y:expr, $eps:expr) => {
let x = $x;
let y = $y;
let eps = $eps;
let diff = (x - y).abs();
if diff > eps {
panic!("assertion failed: `(left !== right)` (left: `{:?}`, right: `{:?}`, eps: `{:?}`, diff: `{:?}`)", x, y, eps, diff);
}
};
}

This macro can then be used in unit tests as follows:

#[test]
fn test_approx_eq() {
assert_approx_eq!(1.0, 1.001, 0.01);
}

Another use case for macros is to define custom datatypes with associated functions. For example, the following macro defines a vector type with an add function:

macro_rules! vector {
($($x:expr),*) => {
{
let mut temp_vec = Vec::new();
$(…

--

--