Skip to content

Commit ef42a1c

Browse files
authored
Merge pull request #948 from mark-i-m/macros
Update macros chapter to address why/when to use
2 parents 343ac55 + 970b09b commit ef42a1c

File tree

5 files changed

+111
-4
lines changed

5 files changed

+111
-4
lines changed

src/SUMMARY.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,13 @@
130130
- [Clone](trait/clone.md)
131131

132132
- [macro_rules!](macros.md)
133-
- [Designators](macros/designators.md)
134-
- [Overload](macros/overload.md)
135-
- [Repeat](macros/repeat.md)
133+
- [Syntax](macro/syntax.md)
134+
- [Designators](macros/designators.md)
135+
- [Overload](macros/overload.md)
136+
- [Repeat](macros/repeat.md)
136137
- [DRY (Don't Repeat Yourself)](macros/dry.md)
138+
- [DSL (Domain Specific Languages)](macros/dsl.md)
139+
- [Variadics](macros/variadics.md)
137140

138141
- [Error handling](error.md)
139142
- [`panic`](error/panic.md)

src/macros.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ Rust provides a powerful macro system that allows metaprogramming. As you've
44
seen in previous chapters, macros look like functions, except that their name
55
ends with a bang `!`, but instead of generating a function call, macros are
66
expanded into source code that gets compiled with the rest of the program.
7+
However, unlike macros in C and other languages, Rust macros are expanded into
8+
abstract syntax trees, rather than string preprocessing, so you don't get
9+
unexpected precedence bugs.
710

811
Macros are created using the `macro_rules!` macro.
912

@@ -21,4 +24,17 @@ fn main() {
2124
// This call will expand into `println!("Hello");`
2225
say_hello!()
2326
}
24-
```
27+
```
28+
29+
So why are macros useful?
30+
31+
1. Don't repeat yourself. There are many cases where you may need similar
32+
functionality in multiple places but with different types. Often, writing a
33+
macro is a useful wait to avoid repeating code. (More on this later)
34+
35+
2. Domain-specific languages. Macros allow you to define special syntax for a
36+
specific purpose. (More on this later)
37+
38+
3. Variadic interfaces. Sometime you want to define an interface that takes a
39+
variable number of arguments. An example is `println!` which could take any
40+
number of arguments, depending on the format string!. (More on this later)

src/macros/dsl.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Domain Specific Languages (DSLs)
2+
3+
A DSL is a mini "language" embedded in a Rust macro. It is completely valid
4+
Rust because the macro system expands into normal Rust constructs, but it looks
5+
like a small language. This allows you to define concise or intuitive syntax for
6+
some special functionality (within bounds).
7+
8+
Suppose that I want to define a little calculator API. I would like to supply
9+
an expression an have the output printed to console.
10+
11+
```rust,editable
12+
macro_rules! calculate {
13+
(eval $e:expr) => {{
14+
{
15+
let val: usize = $e; // Force types to be integers
16+
println!("{} = {}", stringify!{$e}, val);
17+
}
18+
}};
19+
}
20+
21+
fn main() {
22+
calculate! {
23+
eval 1 + 2 // hehehe `eval` is _not_ a Rust keyword!
24+
}
25+
26+
calculate! {
27+
eval (1 + 2) * (3 / 4)
28+
}
29+
}
30+
```
31+
32+
Output:
33+
```
34+
1 + 2 = 3
35+
(1 + 2) * (3 / 4) = 0
36+
```
37+
38+
This was a very simple example, but much more complex interfaces have been
39+
developed, such as [`lazy_static`](https://crates.io/crates/lazy_static) or
40+
[`clap`](https://crates.io/crates/clap).

src/macros/syntax.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Syntax
2+
3+
In following subsections, we will show how to define macros in Rust.
4+
There are three basic ideas:
5+
6+
- [Patterns and Designators](designators.md)
7+
- [Overloading](overload.md)
8+
- [Repetition](repeat.md)

src/macros/variadics.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Variadic Interfaces
2+
3+
A _variadic_ interface takes an arbitrary number of arguments. For example,
4+
`println!` can take an arbitrary number of arguments, as determined by the
5+
format string.
6+
7+
We can extend our `calculate!` macro from the previous section to be variadic:
8+
9+
```rust,editable
10+
macro_rules! calculate {
11+
// The pattern for a single `eval`
12+
(eval $e:expr) => {{
13+
{
14+
let val: usize = $e; // Force types to be integers
15+
println!("{} = {}", stringify!{$e}, val);
16+
}
17+
}};
18+
19+
// Decompose multiple `eval`s recursively
20+
(eval $e:expr, $(eval $es:expr),+) => {{
21+
calculate! { eval $e }
22+
calculate! { $(eval $es),+ }
23+
}};
24+
}
25+
26+
fn main() {
27+
calculate! { // Look ma! Variadic `calculate!`!
28+
eval 1 + 2,
29+
eval 3 + 4,
30+
eval (2 * 3) + 1
31+
}
32+
}
33+
```
34+
35+
Output:
36+
```
37+
1 + 2 = 3
38+
3 + 4 = 7
39+
(2 * 3) + 1 = 7
40+
```

0 commit comments

Comments
 (0)