Exercise 1: My First Macro
Welcome to this introduction to Rust's Macro system. To complete each exercise (including this one), you should:
- Read this file to understand the theory being tested, and what task you will be asked to complete.
-
Try and complete the
main.rs
file. -
Test to see if your macro creates the same code we have, using
cargo run -- test 01_my_first_macro
. -
Run your code, using
cargo run --bin 01_my_first_macro
, to see what it does.
What are Macros?
Rust's macros are a way of using code to generate code before compilation. Because the generation happens before the compiler does anything, you are given much more flexibility in what you can write.
This allows you to break many of the syntax rules Rust imposes on you. For
example, Rust does not allow "variadic" functions: functions with variable
numbers of arguments. This makes a println
function impossible -- it would
have to take any number of arguments (println("hello")
and println("{}", 123)
, for example).
Rust gets around this rule by using a println!
macro. Before println!
is
compiled, Rust rewrites the macro into a function which takes a single array of
arguments. That way, even though it looks to you like there are multiple
arguments, once it's compiled there's always just one array.
Macros can range from simple (e.g. reducing duplicated code) to complex (e.g. implementing HTML parsing inside of Rust). This guide aims to build you up from the simple to the complex.
As mentioned, you've already used macros: println!
for example, is a macro.
vec![]
is as well. Macros always have a name. To run a macro, call its name
with a bang (!
) afterwards, and then brackets (any of ()
, []
or {}
)
containing arguments.
In other words, to run the macro my_macro
, you'd say my_macro!()
or
my_macro![]
or my_macro!{}
.
Macro Rules vs. Procedural Macros
Rust has two macros systems, but this guide will only focus on one.
macro_rules!
are a special language to describe how to transform
code into valid Rust code: this is the system we will focus on.
Procedural macros (proc-macros) are a method of writing a Rust function
which transforms an input piece of Rust code into an output piece.
Proc Macros are useful, but complex, and not the subject of this guide. You can read more about them here.
How do I create one?
The simplest form of macro looks like this:
macro_rules! my_macro { () => { 3 } } fn main() { let _value = my_macro!(); }
The macro_rules!
instructs the compiler that there is a new macro you are
defining. It is followed by the name of the macro, my_macro
. The next line
specifies a "rule". Inside the normal brackets is a "matcher" -- some text
(formally, we refer to the text as "tokens") -- which Rust will use to decide
which rule to execute. Inside the curly brackets is a "transcriber", which is
what Rust will replace my_macro!()
with.
So, my_macro!()
will be replaced by 3
.
Exercise 1: My First Macro
Your task is to write a macro named show_output!()
which calls the
show_output()
function.
You may not edit the main
function, but it should eventually look like the
following:
fn main() {
show_output()
}