]>
Commit | Line | Data |
---|---|---|
13cf67c4 | 1 | ## Macros |
83c7162d | 2 | |
13cf67c4 | 3 | We’ve used macros like `println!` throughout this book, but we haven’t fully |
532ac7d7 XL |
4 | explored what a macro is and how it works. The term *macro* refers to a family |
5 | of features in Rust: *declarative* macros with `macro_rules!` and three kinds | |
6 | of *procedural* macros: | |
83c7162d | 7 | |
532ac7d7 XL |
8 | * Custom `#[derive]` macros that specify code added with the `derive` attribute |
9 | used on structs and enums | |
10 | * Attribute-like macros that define custom attributes usable on any item | |
11 | * Function-like macros that look like function calls but operate on the tokens | |
12 | specified as their argument | |
83c7162d | 13 | |
532ac7d7 XL |
14 | We’ll talk about each of these in turn, but first, let’s look at why we even |
15 | need macros when we already have functions. | |
83c7162d XL |
16 | |
17 | ### The Difference Between Macros and Functions | |
18 | ||
94b46f34 | 19 | Fundamentally, macros are a way of writing code that writes other code, which |
13cf67c4 | 20 | is known as *metaprogramming*. In Appendix C, we discuss the `derive` |
94b46f34 XL |
21 | attribute, which generates an implementation of various traits for you. We’ve |
22 | also used the `println!` and `vec!` macros throughout the book. All of these | |
23 | macros *expand* to produce more code than the code you’ve written manually. | |
83c7162d XL |
24 | |
25 | Metaprogramming is useful for reducing the amount of code you have to write and | |
26 | maintain, which is also one of the roles of functions. However, macros have | |
532ac7d7 | 27 | some additional powers that functions don’t. |
83c7162d | 28 | |
94b46f34 | 29 | A function signature must declare the number and type of parameters the |
83c7162d | 30 | function has. Macros, on the other hand, can take a variable number of |
94b46f34 | 31 | parameters: we can call `println!("hello")` with one argument or |
83c7162d XL |
32 | `println!("hello {}", name)` with two arguments. Also, macros are expanded |
33 | before the compiler interprets the meaning of the code, so a macro can, for | |
34 | example, implement a trait on a given type. A function can’t, because it gets | |
35 | called at runtime and a trait needs to be implemented at compile time. | |
36 | ||
94b46f34 XL |
37 | The downside to implementing a macro instead of a function is that macro |
38 | definitions are more complex than function definitions because you’re writing | |
39 | Rust code that writes Rust code. Due to this indirection, macro definitions are | |
40 | generally more difficult to read, understand, and maintain than function | |
41 | definitions. | |
83c7162d | 42 | |
532ac7d7 XL |
43 | Another important difference between macros and functions is that you must |
44 | define macros or bring them into scope *before* you call them in a file, as | |
45 | opposed to functions you can define anywhere and call anywhere. | |
83c7162d XL |
46 | |
47 | ### Declarative Macros with `macro_rules!` for General Metaprogramming | |
48 | ||
532ac7d7 XL |
49 | The most widely used form of macros in Rust is *declarative macros*. These are |
50 | also sometimes referred to as “macros by example,” “`macro_rules!` macros,” or | |
51 | just plain “macros.” At their core, declarative macros allow you to write | |
52 | something similar to a Rust `match` expression. As discussed in Chapter 6, | |
53 | `match` expressions are control structures that take an expression, compare the | |
83c7162d | 54 | resulting value of the expression to patterns, and then run the code associated |
532ac7d7 XL |
55 | with the matching pattern. Macros also compare a value to patterns that are |
56 | associated with particular code: in this situation, the value is the literal | |
57 | Rust source code passed to the macro; the patterns are compared with the | |
58 | structure of that source code; and the code associated with each pattern, when | |
59 | matched, replaces the code passed to the macro. This all happens during | |
60 | compilation. | |
83c7162d XL |
61 | |
62 | To define a macro, you use the `macro_rules!` construct. Let’s explore how to | |
94b46f34 XL |
63 | use `macro_rules!` by looking at how the `vec!` macro is defined. Chapter 8 |
64 | covered how we can use the `vec!` macro to create a new vector with particular | |
532ac7d7 XL |
65 | values. For example, the following macro creates a new vector containing three |
66 | integers: | |
83c7162d XL |
67 | |
68 | ```rust | |
69 | let v: Vec<u32> = vec![1, 2, 3]; | |
70 | ``` | |
71 | ||
72 | We could also use the `vec!` macro to make a vector of two integers or a vector | |
94b46f34 | 73 | of five string slices. We wouldn’t be able to use a function to do the same |
83c7162d XL |
74 | because we wouldn’t know the number or type of values up front. |
75 | ||
48663c56 | 76 | Listing 19-28 shows a slightly simplified definition of the `vec!` macro. |
13cf67c4 XL |
77 | |
78 | <span class="filename">Filename: src/lib.rs</span> | |
83c7162d XL |
79 | |
80 | ```rust | |
74b04a01 | 81 | {{#rustdoc_include ../listings/ch19-advanced-features/listing-19-28/src/lib.rs}} |
83c7162d XL |
82 | ``` |
83 | ||
48663c56 | 84 | <span class="caption">Listing 19-28: A simplified version of the `vec!` macro |
94b46f34 | 85 | definition</span> |
83c7162d | 86 | |
94b46f34 XL |
87 | > Note: The actual definition of the `vec!` macro in the standard library |
88 | > includes code to preallocate the correct amount of memory up front. That code | |
89 | > is an optimization that we don’t include here to make the example simpler. | |
83c7162d XL |
90 | |
91 | The `#[macro_export]` annotation indicates that this macro should be made | |
532ac7d7 | 92 | available whenever the crate in which the macro is defined is brought into |
13cf67c4 | 93 | scope. Without this annotation, the macro can’t be brought into scope. |
83c7162d XL |
94 | |
95 | We then start the macro definition with `macro_rules!` and the name of the | |
94b46f34 XL |
96 | macro we’re defining *without* the exclamation mark. The name, in this case |
97 | `vec`, is followed by curly brackets denoting the body of the macro definition. | |
83c7162d XL |
98 | |
99 | The structure in the `vec!` body is similar to the structure of a `match` | |
100 | expression. Here we have one arm with the pattern `( $( $x:expr ),* )`, | |
101 | followed by `=>` and the block of code associated with this pattern. If the | |
102 | pattern matches, the associated block of code will be emitted. Given that this | |
94b46f34 | 103 | is the only pattern in this macro, there is only one valid way to match; any |
532ac7d7 XL |
104 | other pattern will result in an error. More complex macros will have more than |
105 | one arm. | |
83c7162d XL |
106 | |
107 | Valid pattern syntax in macro definitions is different than the pattern syntax | |
108 | covered in Chapter 18 because macro patterns are matched against Rust code | |
532ac7d7 | 109 | structure rather than values. Let’s walk through what the pattern pieces in |
48663c56 | 110 | Listing 19-28 mean; for the full macro pattern syntax, see [the reference]. |
83c7162d | 111 | |
74b04a01 | 112 | [the reference]: ../reference/macros-by-example.html |
83c7162d | 113 | |
532ac7d7 XL |
114 | First, a set of parentheses encompasses the whole pattern. A dollar sign (`$`) |
115 | is next, followed by a set of parentheses that captures values that match the | |
116 | pattern within the parentheses for use in the replacement code. Within `$()` is | |
117 | `$x:expr`, which matches any Rust expression and gives the expression the name | |
118 | `$x`. | |
83c7162d XL |
119 | |
120 | The comma following `$()` indicates that a literal comma separator character | |
532ac7d7 XL |
121 | could optionally appear after the code that matches the code in `$()`. The `*` |
122 | specifies that the pattern matches zero or more of whatever precedes the `*`. | |
83c7162d XL |
123 | |
124 | When we call this macro with `vec![1, 2, 3];`, the `$x` pattern matches three | |
125 | times with the three expressions `1`, `2`, and `3`. | |
126 | ||
127 | Now let’s look at the pattern in the body of the code associated with this arm: | |
532ac7d7 XL |
128 | `temp_vec.push()` within `$()*` is generated for each part that matches `$()` |
129 | in the pattern zero or more times depending on how many times the pattern | |
130 | matches. The `$x` is replaced with each expression matched. When we call this | |
131 | macro with `vec![1, 2, 3];`, the code generated that replaces this macro call | |
132 | will be the following: | |
83c7162d XL |
133 | |
134 | ```rust,ignore | |
74b04a01 XL |
135 | { |
136 | let mut temp_vec = Vec::new(); | |
137 | temp_vec.push(1); | |
138 | temp_vec.push(2); | |
139 | temp_vec.push(3); | |
140 | temp_vec | |
141 | } | |
83c7162d XL |
142 | ``` |
143 | ||
144 | We’ve defined a macro that can take any number of arguments of any type and can | |
145 | generate code to create a vector containing the specified elements. | |
146 | ||
532ac7d7 XL |
147 | There are some strange edge cases with `macro_rules!`. In the future, Rust will |
148 | have a second kind of declarative macro that will work in a similar fashion but | |
149 | fix some of these edge cases. After that update, `macro_rules!` will be | |
150 | effectively deprecated. With this in mind, as well as the fact that most Rust | |
151 | programmers will *use* macros more than *write* macros, we won’t discuss | |
152 | `macro_rules!` any further. To learn more about how to write macros, consult | |
153 | the online documentation or other resources, such as [“The Little Book of Rust | |
154 | Macros”][tlborm]. | |
83c7162d XL |
155 | |
156 | [tlborm]: https://danielkeep.github.io/tlborm/book/index.html | |
157 | ||
13cf67c4 | 158 | ### Procedural Macros for Generating Code from Attributes |
83c7162d | 159 | |
532ac7d7 XL |
160 | The second form of macros is *procedural macros*, which act more like functions |
161 | (and are a type of procedure). Procedural macros accept some code as an input, | |
162 | operate on that code, and produce some code as an output rather than matching | |
163 | against patterns and replacing the code with other code as declarative macros | |
164 | do. | |
13cf67c4 | 165 | |
dc9dc135 XL |
166 | The three kinds of procedural macros (custom derive, attribute-like, and |
167 | function-like) all work in a similar fashion. | |
13cf67c4 | 168 | |
532ac7d7 XL |
169 | When creating procedural macros, the definitions must reside in their own crate |
170 | with a special crate type. This is for complex technical reasons that we hope | |
171 | to eliminate in the future. Using procedural macros looks like the code in | |
48663c56 | 172 | Listing 19-29, where `some_attribute` is a placeholder for using a specific |
13cf67c4 XL |
173 | macro. |
174 | ||
175 | <span class="filename">Filename: src/lib.rs</span> | |
176 | ||
177 | ```rust,ignore | |
178 | use proc_macro; | |
83c7162d | 179 | |
13cf67c4 XL |
180 | #[some_attribute] |
181 | pub fn some_name(input: TokenStream) -> TokenStream { | |
182 | } | |
183 | ``` | |
184 | ||
48663c56 | 185 | <span class="caption">Listing 19-29: An example of using a procedural |
13cf67c4 XL |
186 | macro</span> |
187 | ||
532ac7d7 XL |
188 | The function that defines a procedural macro takes a `TokenStream` as an input |
189 | and produces a `TokenStream` as an output. The `TokenStream` type is defined by | |
190 | the `proc_macro` crate that is included with Rust and represents a sequence of | |
191 | tokens. This is the core of the macro: the source code that the macro is | |
192 | operating on makes up the input `TokenStream`, and the code the macro produces | |
193 | is the output `TokenStream`. The function also has an attribute attached to it | |
194 | that specifies which kind of procedural macro we’re creating. We can have | |
195 | multiple kinds of procedural macros in the same crate. | |
196 | ||
197 | Let’s look at the different kinds of procedural macros. We’ll start with a | |
198 | custom derive macro and then explain the small dissimilarities that make the | |
199 | other forms different. | |
13cf67c4 XL |
200 | |
201 | ### How to Write a Custom `derive` Macro | |
202 | ||
203 | Let’s create a crate named `hello_macro` that defines a trait named | |
83c7162d | 204 | `HelloMacro` with one associated function named `hello_macro`. Rather than |
94b46f34 | 205 | making our crate users implement the `HelloMacro` trait for each of their |
83c7162d XL |
206 | types, we’ll provide a procedural macro so users can annotate their type with |
207 | `#[derive(HelloMacro)]` to get a default implementation of the `hello_macro` | |
208 | function. The default implementation will print `Hello, Macro! My name is | |
209 | TypeName!` where `TypeName` is the name of the type on which this trait has | |
94b46f34 | 210 | been defined. In other words, we’ll write a crate that enables another |
48663c56 | 211 | programmer to write code like Listing 19-30 using our crate. |
83c7162d XL |
212 | |
213 | <span class="filename">Filename: src/main.rs</span> | |
214 | ||
74b04a01 XL |
215 | ```rust,ignore,does_not_compile |
216 | {{#rustdoc_include ../listings/ch19-advanced-features/listing-19-30/src/main.rs}} | |
83c7162d XL |
217 | ``` |
218 | ||
48663c56 | 219 | <span class="caption">Listing 19-30: The code a user of our crate will be able |
13cf67c4 | 220 | to write when using our procedural macro</span> |
83c7162d | 221 | |
94b46f34 XL |
222 | This code will print `Hello, Macro! My name is Pancakes!` when we’re done. The |
223 | first step is to make a new library crate, like this: | |
83c7162d | 224 | |
f035d41b | 225 | ```console |
83c7162d XL |
226 | $ cargo new hello_macro --lib |
227 | ``` | |
228 | ||
94b46f34 | 229 | Next, we’ll define the `HelloMacro` trait and its associated function: |
83c7162d XL |
230 | |
231 | <span class="filename">Filename: src/lib.rs</span> | |
232 | ||
233 | ```rust | |
74b04a01 | 234 | {{#rustdoc_include ../listings/ch19-advanced-features/no-listing-20-impl-hellomacro-for-pancakes/hello_macro/src/lib.rs}} |
83c7162d XL |
235 | ``` |
236 | ||
94b46f34 XL |
237 | We have a trait and its function. At this point, our crate user could implement |
238 | the trait to achieve the desired functionality, like so: | |
83c7162d XL |
239 | |
240 | ```rust,ignore | |
74b04a01 | 241 | {{#rustdoc_include ../listings/ch19-advanced-features/no-listing-20-impl-hellomacro-for-pancakes/pancakes/src/main.rs}} |
83c7162d XL |
242 | ``` |
243 | ||
94b46f34 XL |
244 | However, they would need to write the implementation block for each type they |
245 | wanted to use with `hello_macro`; we want to spare them from having to do this | |
246 | work. | |
83c7162d | 247 | |
532ac7d7 XL |
248 | Additionally, we can’t yet provide the `hello_macro` function with default |
249 | implementation that will print the name of the type the trait is implemented | |
250 | on: Rust doesn’t have reflection capabilities, so it can’t look up the type’s | |
251 | name at runtime. We need a macro to generate code at compile time. | |
83c7162d | 252 | |
94b46f34 | 253 | The next step is to define the procedural macro. At the time of this writing, |
83c7162d | 254 | procedural macros need to be in their own crate. Eventually, this restriction |
94b46f34 XL |
255 | might be lifted. The convention for structuring crates and macro crates is as |
256 | follows: for a crate named `foo`, a custom derive procedural macro crate is | |
257 | called `foo_derive`. Let’s start a new crate called `hello_macro_derive` inside | |
258 | our `hello_macro` project: | |
83c7162d | 259 | |
f035d41b | 260 | ```console |
83c7162d XL |
261 | $ cargo new hello_macro_derive --lib |
262 | ``` | |
263 | ||
264 | Our two crates are tightly related, so we create the procedural macro crate | |
265 | within the directory of our `hello_macro` crate. If we change the trait | |
266 | definition in `hello_macro`, we’ll have to change the implementation of the | |
267 | procedural macro in `hello_macro_derive` as well. The two crates will need to | |
94b46f34 XL |
268 | be published separately, and programmers using these crates will need to add |
269 | both as dependencies and bring them both into scope. We could instead have the | |
416331ca | 270 | `hello_macro` crate use `hello_macro_derive` as a dependency and re-export the |
532ac7d7 | 271 | procedural macro code. However, the way we’ve structured the project makes it |
94b46f34 XL |
272 | possible for programmers to use `hello_macro` even if they don’t want the |
273 | `derive` functionality. | |
83c7162d XL |
274 | |
275 | We need to declare the `hello_macro_derive` crate as a procedural macro crate. | |
94b46f34 | 276 | We’ll also need functionality from the `syn` and `quote` crates, as you’ll see |
83c7162d XL |
277 | in a moment, so we need to add them as dependencies. Add the following to the |
278 | *Cargo.toml* file for `hello_macro_derive`: | |
279 | ||
280 | <span class="filename">Filename: hello_macro_derive/Cargo.toml</span> | |
281 | ||
282 | ```toml | |
74b04a01 | 283 | {{#include ../listings/ch19-advanced-features/listing-19-31/hello_macro/hello_macro_derive/Cargo.toml:7:12}} |
83c7162d XL |
284 | ``` |
285 | ||
48663c56 | 286 | To start defining the procedural macro, place the code in Listing 19-31 into |
13cf67c4 XL |
287 | your *src/lib.rs* file for the `hello_macro_derive` crate. Note that this code |
288 | won’t compile until we add a definition for the `impl_hello_macro` function. | |
83c7162d | 289 | |
83c7162d XL |
290 | <span class="filename">Filename: hello_macro_derive/src/lib.rs</span> |
291 | ||
74b04a01 XL |
292 | ```rust,ignore,does_not_compile |
293 | {{#rustdoc_include ../listings/ch19-advanced-features/listing-19-31/hello_macro/hello_macro_derive/src/lib.rs}} | |
83c7162d XL |
294 | ``` |
295 | ||
48663c56 | 296 | <span class="caption">Listing 19-31: Code that most procedural macro crates |
532ac7d7 | 297 | will require in order to process Rust code</span> |
83c7162d | 298 | |
dc9dc135 XL |
299 | Notice that we’ve split the code into the `hello_macro_derive` function, which |
300 | is responsible for parsing the `TokenStream`, and the `impl_hello_macro` | |
301 | function, which is responsible for transforming the syntax tree: this makes | |
302 | writing a procedural macro more convenient. The code in the outer function | |
303 | (`hello_macro_derive` in this case) will be the same for almost every | |
304 | procedural macro crate you see or create. The code you specify in the body of | |
305 | the inner function (`impl_hello_macro` in this case) will be different | |
306 | depending on your procedural macro’s purpose. | |
94b46f34 XL |
307 | |
308 | We’ve introduced three new crates: `proc_macro`, [`syn`], and [`quote`]. The | |
83c7162d | 309 | `proc_macro` crate comes with Rust, so we didn’t need to add that to the |
532ac7d7 XL |
310 | dependencies in *Cargo.toml*. The `proc_macro` crate is the compiler’s API that |
311 | allows us to read and manipulate Rust code from our code. | |
83c7162d XL |
312 | |
313 | [`syn`]: https://crates.io/crates/syn | |
314 | [`quote`]: https://crates.io/crates/quote | |
315 | ||
532ac7d7 XL |
316 | The `syn` crate parses Rust code from a string into a data structure that we |
317 | can perform operations on. The `quote` crate turns `syn` data structures back | |
318 | into Rust code. These crates make it much simpler to parse any sort of Rust | |
319 | code we might want to handle: writing a full parser for Rust code is no simple | |
320 | task. | |
321 | ||
322 | The `hello_macro_derive` function will be called when a user of our library | |
323 | specifies `#[derive(HelloMacro)]` on a type. This is possible because we’ve | |
324 | annotated the `hello_macro_derive` function here with `proc_macro_derive` and | |
325 | specified the name, `HelloMacro`, which matches our trait name; this is the | |
326 | convention most procedural macros follow. | |
327 | ||
328 | The `hello_macro_derive` function first converts the `input` from a | |
329 | `TokenStream` to a data structure that we can then interpret and perform | |
330 | operations on. This is where `syn` comes into play. The `parse` function in | |
331 | `syn` takes a `TokenStream` and returns a `DeriveInput` struct representing the | |
48663c56 | 332 | parsed Rust code. Listing 19-32 shows the relevant parts of the `DeriveInput` |
532ac7d7 | 333 | struct we get from parsing the `struct Pancakes;` string: |
83c7162d XL |
334 | |
335 | ```rust,ignore | |
336 | DeriveInput { | |
337 | // --snip-- | |
338 | ||
13cf67c4 XL |
339 | ident: Ident { |
340 | ident: "Pancakes", | |
341 | span: #0 bytes(95..103) | |
342 | }, | |
343 | data: Struct( | |
344 | DataStruct { | |
345 | struct_token: Struct, | |
346 | fields: Unit, | |
347 | semi_token: Some( | |
348 | Semi | |
349 | ) | |
350 | } | |
83c7162d XL |
351 | ) |
352 | } | |
353 | ``` | |
354 | ||
48663c56 XL |
355 | <span class="caption">Listing 19-32: The `DeriveInput` instance we get when |
356 | parsing the code that has the macro’s attribute in Listing 19-30</span> | |
13cf67c4 | 357 | |
83c7162d XL |
358 | The fields of this struct show that the Rust code we’ve parsed is a unit struct |
359 | with the `ident` (identifier, meaning the name) of `Pancakes`. There are more | |
360 | fields on this struct for describing all sorts of Rust code; check the [`syn` | |
94b46f34 | 361 | documentation for `DeriveInput`][syn-docs] for more information. |
83c7162d | 362 | |
74b04a01 | 363 | [syn-docs]: https://docs.rs/syn/1.0/syn/struct.DeriveInput.html |
83c7162d | 364 | |
532ac7d7 XL |
365 | Soon we’ll define the `impl_hello_macro` function, which is where we’ll build |
366 | the new Rust code we want to include. But before we do, note that the output | |
367 | for our derive macro is also a `TokenStream`. The returned `TokenStream` is | |
368 | added to the code that our crate users write, so when they compile their crate, | |
369 | they’ll get the extra functionality that we provide in the modified | |
370 | `TokenStream`. | |
371 | ||
372 | You might have noticed that we’re calling `unwrap` to cause the | |
373 | `hello_macro_derive` function to panic if the call to the `syn::parse` function | |
374 | fails here. It’s necessary for our procedural macro to panic on errors because | |
375 | `proc_macro_derive` functions must return `TokenStream` rather than `Result` to | |
376 | conform to the procedural macro API. We’ve simplified this example by using | |
377 | `unwrap`; in production code, you should provide more specific error messages | |
378 | about what went wrong by using `panic!` or `expect`. | |
83c7162d XL |
379 | |
380 | Now that we have the code to turn the annotated Rust code from a `TokenStream` | |
8faf50e0 | 381 | into a `DeriveInput` instance, let’s generate the code that implements the |
48663c56 | 382 | `HelloMacro` trait on the annotated type, as shown in Listing 19-33. |
83c7162d XL |
383 | |
384 | <span class="filename">Filename: hello_macro_derive/src/lib.rs</span> | |
385 | ||
386 | ```rust,ignore | |
74b04a01 | 387 | {{#rustdoc_include ../listings/ch19-advanced-features/listing-19-33/hello_macro/hello_macro_derive/src/lib.rs:here}} |
83c7162d XL |
388 | ``` |
389 | ||
48663c56 | 390 | <span class="caption">Listing 19-33: Implementing the `HelloMacro` trait using |
13cf67c4 | 391 | the parsed Rust code</span> |
8faf50e0 | 392 | |
13cf67c4 | 393 | We get an `Ident` struct instance containing the name (identifier) of the |
48663c56 XL |
394 | annotated type using `ast.ident`. The struct in Listing 19-32 shows that when |
395 | we run the `impl_hello_macro` function on the code in Listing 19-30, the | |
532ac7d7 | 396 | `ident` we get will have the `ident` field with a value of `"Pancakes"`. Thus, |
48663c56 | 397 | the `name` variable in Listing 19-33 will contain an `Ident` struct instance |
13cf67c4 | 398 | that, when printed, will be the string `"Pancakes"`, the name of the struct in |
48663c56 | 399 | Listing 19-30. |
13cf67c4 | 400 | |
532ac7d7 XL |
401 | The `quote!` macro lets us define the Rust code that we want to return. The |
402 | compiler expects something different to the direct result of the `quote!` | |
403 | macro’s execution, so we need to convert it to a `TokenStream`. We do this by | |
404 | calling the `into` method, which consumes this intermediate representation and | |
405 | returns a value of the required `TokenStream` type. | |
13cf67c4 | 406 | |
532ac7d7 XL |
407 | The `quote!` macro also provides some very cool templating mechanics: we can |
408 | enter `#name`, and `quote!` will replace it with the value in the variable | |
409 | `name`. You can even do some repetition similar to the way regular macros work. | |
410 | Check out [the `quote` crate’s docs][quote-docs] for a thorough introduction. | |
83c7162d XL |
411 | |
412 | [quote-docs]: https://docs.rs/quote | |
413 | ||
414 | We want our procedural macro to generate an implementation of our `HelloMacro` | |
415 | trait for the type the user annotated, which we can get by using `#name`. The | |
416 | trait implementation has one function, `hello_macro`, whose body contains the | |
417 | functionality we want to provide: printing `Hello, Macro! My name is` and then | |
418 | the name of the annotated type. | |
419 | ||
420 | The `stringify!` macro used here is built into Rust. It takes a Rust | |
421 | expression, such as `1 + 2`, and at compile time turns the expression into a | |
422 | string literal, such as `"1 + 2"`. This is different than `format!` or | |
532ac7d7 XL |
423 | `println!`, macros which evaluate the expression and then turn the result into |
424 | a `String`. There is a possibility that the `#name` input might be an | |
425 | expression to print literally, so we use `stringify!`. Using `stringify!` also | |
426 | saves an allocation by converting `#name` to a string literal at compile time. | |
83c7162d XL |
427 | |
428 | At this point, `cargo build` should complete successfully in both `hello_macro` | |
13cf67c4 | 429 | and `hello_macro_derive`. Let’s hook up these crates to the code in Listing |
48663c56 | 430 | 19-30 to see the procedural macro in action! Create a new binary project in |
13cf67c4 | 431 | your *projects* directory using `cargo new pancakes`. We need to add |
94b46f34 XL |
432 | `hello_macro` and `hello_macro_derive` as dependencies in the `pancakes` |
433 | crate’s *Cargo.toml*. If you’re publishing your versions of `hello_macro` and | |
dc9dc135 | 434 | `hello_macro_derive` to [crates.io](https://crates.io/), they would be regular |
94b46f34 | 435 | dependencies; if not, you can specify them as `path` dependencies as follows: |
83c7162d XL |
436 | |
437 | ```toml | |
74b04a01 | 438 | {{#include ../listings/ch19-advanced-features/no-listing-21-pancakes/pancakes/Cargo.toml:7:9}} |
83c7162d XL |
439 | ``` |
440 | ||
48663c56 | 441 | Put the code in Listing 19-30 into *src/main.rs*, and run `cargo run`: it |
94b46f34 | 442 | should print `Hello, Macro! My name is Pancakes!` The implementation of the |
83c7162d | 443 | `HelloMacro` trait from the procedural macro was included without the |
94b46f34 XL |
444 | `pancakes` crate needing to implement it; the `#[derive(HelloMacro)]` added the |
445 | trait implementation. | |
83c7162d | 446 | |
13cf67c4 XL |
447 | Next, let’s explore how the other kinds of procedural macros differ from custom |
448 | derive macros. | |
449 | ||
450 | ### Attribute-like macros | |
451 | ||
452 | Attribute-like macros are similar to custom derive macros, but instead of | |
453 | generating code for the `derive` attribute, they allow you to create new | |
532ac7d7 XL |
454 | attributes. They’re also more flexible: `derive` only works for structs and |
455 | enums; attributes can be applied to other items as well, such as functions. | |
456 | Here’s an example of using an attribute-like macro: say you have an attribute | |
457 | named `route` that annotates functions when using a web application framework: | |
13cf67c4 XL |
458 | |
459 | ```rust,ignore | |
460 | #[route(GET, "/")] | |
461 | fn index() { | |
462 | ``` | |
463 | ||
532ac7d7 XL |
464 | This `#[route]` attribute would be defined by the framework as a procedural |
465 | macro. The signature of the macro definition function would look like this: | |
13cf67c4 XL |
466 | |
467 | ```rust,ignore | |
468 | #[proc_macro_attribute] | |
469 | pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream { | |
470 | ``` | |
471 | ||
532ac7d7 XL |
472 | Here, we have two parameters of type `TokenStream`. The first is for the |
473 | contents of the attribute: the `GET, "/"` part. The second is the body of the | |
474 | item the attribute is attached to: in this case, `fn index() {}` and the rest | |
475 | of the function’s body. | |
13cf67c4 XL |
476 | |
477 | Other than that, attribute-like macros work the same way as custom derive | |
532ac7d7 | 478 | macros: you create a crate with the `proc-macro` crate type and implement a |
13cf67c4 XL |
479 | function that generates the code you want! |
480 | ||
481 | ### Function-like macros | |
482 | ||
532ac7d7 | 483 | Function-like macros define macros that look like function calls. Similarly to |
dc9dc135 XL |
484 | `macro_rules!` macros, they’re more flexible than functions; for example, they |
485 | can take an unknown number of arguments. However, `macro_rules!` macros can be | |
486 | defined only using the match-like syntax we discussed in the section | |
487 | [“Declarative Macros with `macro_rules!` for General Metaprogramming”][decl] | |
488 | earlier. Function-like macros take a `TokenStream` parameter and their | |
489 | definition manipulates that `TokenStream` using Rust code as the other two | |
490 | types of procedural macros do. An example of a function-like macro is an `sql!` | |
491 | macro that might be called like so: | |
532ac7d7 XL |
492 | |
493 | [decl]: #declarative-macros-with-macro_rules-for-general-metaprogramming | |
13cf67c4 XL |
494 | |
495 | ```rust,ignore | |
496 | let sql = sql!(SELECT * FROM posts WHERE id=1); | |
497 | ``` | |
498 | ||
532ac7d7 XL |
499 | This macro would parse the SQL statement inside it and check that it’s |
500 | syntactically correct, which is much more complex processing than a | |
501 | `macro_rules!` macro can do. The `sql!` macro would be defined like this: | |
13cf67c4 XL |
502 | |
503 | ```rust,ignore | |
504 | #[proc_macro] | |
505 | pub fn sql(input: TokenStream) -> TokenStream { | |
506 | ``` | |
507 | ||
532ac7d7 XL |
508 | This definition is similar to the custom derive macro’s signature: we receive |
509 | the tokens that are inside the parentheses and return the code we wanted to | |
510 | generate. | |
13cf67c4 XL |
511 | |
512 | ## Summary | |
513 | ||
532ac7d7 | 514 | Whew! Now you have some Rust features in your toolbox that you won’t use often, |
dc9dc135 XL |
515 | but you’ll know they’re available in very particular circumstances. We’ve |
516 | introduced several complex topics so that when you encounter them in error | |
517 | message suggestions or in other peoples’ code, you’ll be able to recognize | |
518 | these concepts and syntax. Use this chapter as a reference to guide you to | |
519 | solutions. | |
83c7162d | 520 | |
13cf67c4 XL |
521 | Next, we’ll put everything we’ve discussed throughout the book into practice |
522 | and do one more project! |