Macros: The Hidden Power of Code Efficiency

Chidozie C. Okafor
5 min readJul 21, 2023

--

In programming, simplicity and efficiency are frequently the main objectives. Macros serve a similar function as ordering “the usual” at a restaurant to quickly and effectively express your preference for a meal. The use of macros, a type of code shorthand, allows programmers to build reusable sections of code without having to write the same code over. The universe of macros, their sophisticated use cases, and the reasons they are crucial in programming — especially in languages like Rust — will all be covered in this article.

Macros: What are they?

Let’s assume you’re a terrific home cook (which we both know that you are not), and you have a certain dish that you love to create, say, your classic spaghetti bolognese. With the ideal balance of fresh herbs, soft ground beef, and a rich, savory sauce, this recipe has your unique spin and is served over freshly cooked spaghetti.

But once in a while, you treat yourself to a lunch at your preferred eatery. And each time you visit, you invariably order the spaghetti bolognese they serve. It’s not quite like the kind you make at home, but it’s still tasty in its own special way. You simply say, “I’ll have the usual,” rather than giving the waiter a list of all the ingredients.

Here, “the usual” is comparable to a programming macro. A set of instructions that you define once and use repeatedly is known as a macro. A macro in programming instructs the computer to carry out a specific set of duties without you having to specify each step individually every time, much like saying “the usual” to the waiter informs him exactly what you want without listing every component.

println!("I don't know how to cook")

In rust, we utilize a println!() macro to print things on the screen, similar to how waiters write down your order to remember it. It’s similar to cooking your favorite dish: once you know the recipe, you can easily reproduce it.

The Power of Macros in Rust

Known for its efficiency and security, Rust is a statically typed, compiled language. It also has a strong macro system, which is well known. The macros in Rust are similar to “functions,” but because they can accept a wide range of inputs and are executed at compile time, they are far more potent. In Rust, macros come in two flavors: declarative macros created with `macro_rules!` and procedural macros, which are more sophisticated and can define functions, generate traits, or implement custom characteristics.

macro_rules! say_hello {
($name:expr) => {
println!("Hello, {}!", $name);
};
}

fn main() {
say_hello!("Alice");
say_hello!("Bob");
}

say_hello! is our custom macro. When we say say_hello!("Alice"), it's like saying, "the usual, for Alice", and the program will print "Hello, Alice!".

Making the Most out of Macros

Macros are much more than a simple tool for avoiding code repetition. Here are some of the more advanced uses of macros.

Conditional Compilation:

When choosing which code to include and which to omit during compilation, macros can be useful. When building code that needs to act differently in development and production settings or needs to support several hardware or software platforms, this capability is essential.

#[cfg(target_os = "windows")]
macro_rules! target_platform {
() => {
"Windows"
};
}

#[cfg(target_os = "linux")]
macro_rules! target_platform {
() => {
"Linux"
};
}

fn main() {
println!("Running on {} platform.", target_platform!());
}

Do you know how some toys behave differently depending on whether you’re inside or outside? Perhaps you have a robot that can only move on your smooth kitchen floor and a toy car that only operates in the sandbox outdoors. Each toy has a unique location where it performs best.

Similar to those gadgets is this piece of code. Similar to how your toys behave differently based on where they are, this program behaves slightly differently depending on where it is utilized.

Here’s how it works:

  • First, it asks, “Am I running on Windows?” (That’s like asking, “Am I playing outside in the sandbox?”). If the answer is “yes,” it chooses the toy car and says, “Windows.”
  • If it’s not running on Windows, it asks, “Am I running on Linux?” (That’s like asking, “Am I playing inside on the kitchen floor?”). If the answer is “yes,” it chooses the robot and says, “Linux.”

Variadic Inputs:

Macros can accept a variable number of arguments, which can be quite handy when you don’t know in advance how many inputs a function might need.

macro_rules! create_array {
($($element:expr),*) => {
[ $($element),* ]
};
}

fn main() {
let arr = create_array!(1, 2, 3, 4, 5);
println!("{:?}", arr);
}

Control Flow Structures:

Macros can also create custom control flow structures that behave differently from the built-in if, for, while, etc. For instance, we could create a repeat! macro that repeats an operation a specified number of times.

macro_rules! repeat {
($count:expr, $action:expr) => {
for _ in 0..$count {
$action;
}
};
}

fn main() {
repeat!(5, println!("I love macros!"));
}

Code Generation:

You can use macros to generate code, like creating new functions or implementing traits for a type.

macro_rules! impl_add_trait_for {
($t:ty) => {
impl std::ops::Add for $t {
type Output = Self;
fn add(self, other: Self) -> Self {
self + other
}
}
};
}

impl_add_trait_for!(i32);
impl_add_trait_for!(f64);

Programming macros are effective tools that can make your code simpler, eliminate repetition, and increase flexibility. They create a quick and easy way to get what you need done, much like ordering the “usual” at your favorite restaurant. Rust’s examples merely scrape the surface of what macros are capable of.

Languages like Rust in particular take advantage of the potential of macros by allowing two types — declarative macros and procedural macros — each of which has certain advantages.

The ability to learn macros, which bridge the gap between code verbosity and brevity while keeping a high level of code functionality and control, is ultimately a crucial talent in the toolbox of modern programming. Macros continue to be a go-to solution for platform-specific chores and code efficiency optimization, making them a magical shortcut you’ll want to use in your programming career.

--

--

Chidozie C. Okafor
Chidozie C. Okafor

Written by Chidozie C. Okafor

Software Engineer & Backend Magician 🎩 | Python, Rust | TypeScript, Node.js | Golang | Kafka & GRPC

No responses yet