macro_rules! double {
// Input parameters
($value:expr)
=>
// Output
($value * 2);
}
fn main() {
let doubled = double!(5);
println!("{}", doubled);
// Alternatives:
double! { 5 };
double![5];
}
Rust has a macro language. See The Little Book Of Macros.
println!(pattern, [values])
Easy printing of formatted strings to stdout
format!(pattern, [values])
like println!
, but returns Strings
write!(buf, pattern, [values])
Simple writing of formatted data to a buffer
Macros can be used to things such as:
Generate repetitive code.
Create DSLs.
Write things that would otherwise be hard without Macros.
Macros are:
Hygienic, expansion happens in a different 'syntax context'
Correct, they cannot expand to invalid code.
Limited, they cannot, for example, pollute their expansion site.
A macro has three parts.
A name, eg. println
.
A input portion, defining what the macro accepts.
An output portion, defining how it expands.
Let’s see how they look:
macro_rules! double {
// Input parameters
($value:expr)
=>
// Output
($value * 2);
}
fn main() {
let doubled = double!(5);
println!("{}", doubled);
// Alternatives:
double! { 5 };
double![5];
}
The ($value:expr)
part says that:
The macro accepts one parameter which is an expression.
Parameter types can be restricted.
For example, $foo:ty
only accepts a type.
The parameters are prepended with a $
to distinguish them.
Both in the input and output.
macro_rules! implement_foo_for {
[
// This is a repetition!
$($implement_for:ty,)*
] => {
// This iterates over the repetition!
$(impl Foo for $implement_for {})*
}
}
implement_foo_for![u8, u16, u32, u64, usize,];
implement_foo_for! { i8, i16, i32, i64, isize, }
implement_foo_for!(f32, f64,);
trait Foo {
fn foo(&self) {
println!("Foo!");
}
}
fn main() {
1_u8.foo();
1_u16.foo();
}
When we see $(…)*
this is signalling a repetition. It communicates:
This portion of the macro takes a variable number of arguments.
Each repetition in the input should have a matching one in the output.
macro_rules! email {
($user:expr => $domain:expr) => {
format!("{}@{}", $user, $domain);
}
}
fn main() {
let address = email!("me" => "example.org");
println!("{}", address);
}
Macros allow for a limited form of custom syntax and can be used to build simple DSLs.
For a good example of this, see clap-rs
Macros:
Can be difficult to debug.
Can be confusing to read and understand.
Use macros where there are no other good alternatives.
Avoid overusing macros.