|
3 | 3 | *Procedural macros* allow creating syntax extensions as execution of a function.
|
4 | 4 | Procedural macros come in one of three flavors:
|
5 | 5 |
|
6 |
| -* Bang macros - `my_macro!(...)` |
7 |
| -* Derive macros - `#[derive(MyTrait)]` |
8 |
| -* Attribute macros - `#[my_attribute]` |
| 6 | +* Bang macros - `custom_bang!(...)` |
| 7 | +* Derive mode macros - `#[derive(CustomMode)]` |
| 8 | +* Attribute macros - `#[CustomAttribute]` |
9 | 9 |
|
10 | 10 | Procedural macros allow you to run code at compile time that operates over Rust
|
11 | 11 | syntax, both consuming and producing Rust syntax. You can sort of think of
|
@@ -150,105 +150,83 @@ pub fn foo(_input: TokenStream) -> TokenStream {
|
150 | 150 | the answer was: 4
|
151 | 151 | ```
|
152 | 152 |
|
153 |
| -### Derive macros |
| 153 | +### Derive mode macros |
154 | 154 |
|
155 |
| -The derive macro feature allows you to define a new `#[derive(Foo)]` mode which |
156 |
| -often makes it much easier to systematically implement traits, removing quite a |
157 |
| -lot of boilerplate. |
| 155 | +*Derive mode macros* define new modes for the `derive` attribute. The macros |
| 156 | +define new items given the token stream of a [struct], [enum], or [union]. They |
| 157 | +also define derive mode helper attributes. |
158 | 158 |
|
159 |
| -Custom derives are defined like so: |
| 159 | +Custom derivers are defined by a [public] [function] with the `proc_maco_derive` |
| 160 | +attribute that takes a single input of the type [`TokenStream`] and returns a |
| 161 | +[`TokenStream`]. |
160 | 162 |
|
161 |
| -```rust,ignore |
162 |
| -#[proc_macro_derive(MyTrait)] |
163 |
| -pub fn foo(item: TokenStream) -> TokenStream { |
164 |
| - // ... |
165 |
| -} |
166 |
| -``` |
167 |
| - |
168 |
| -Here the argument to the `proc_macro_derive` attribute, `MyTrait`, is the name |
169 |
| -of the identifier to pass to `#[derive]`. The name of the function here, `foo`, |
170 |
| -is not currently used (but it may one day be used). |
| 163 | +The input [`TokenStream`] is the token stream of the item that has the `derive` |
| 164 | +attribute on it. The output [`TokenStream`] must be a set of items that are |
| 165 | +then appended to the [module] or [block] that the item from the input |
| 166 | +[`TokenStream`] is in. |
171 | 167 |
|
172 |
| -Like procedural bang macros the input to the macro here is the item that the |
173 |
| -attribute was applied to. Unlike bang macros, however, the output is *appended* |
174 |
| -to the program rather than replacing the item its attached to. We can see this |
175 |
| -behavior by defining a macro like: |
| 168 | +The following is an example of a derive mode macro. Instead of doing anything |
| 169 | +useful with its input, it just appends a function `answer`. |
176 | 170 |
|
177 | 171 | ```rust,ignore
|
178 | 172 | extern crate proc_macro;
|
179 |
| -use proc_macro::*; |
| 173 | +use proc_macro::TokenStream; |
180 | 174 |
|
181 |
| -#[proc_macro_derive(MyTrait)] |
182 |
| -pub fn foo(item: TokenStream) -> TokenStream { |
183 |
| - println!("{:#?}", item); |
184 |
| - "fn answer() -> u32 { 2 }".parse().unwrap() |
| 175 | +#[proc_macro_derive(AnswerFn)] |
| 176 | +pub fn foo(_item: TokenStream) -> TokenStream { |
| 177 | + "fn answer() -> u32 { 42 }".parse().unwrap() |
185 | 178 | }
|
186 | 179 | ```
|
187 | 180 |
|
188 |
| -using it liek: |
| 181 | +And then using said derive mode: |
189 | 182 |
|
190 | 183 | ```rust,ignore
|
191 |
| -extern crate my_macro; |
192 |
| -
|
193 |
| -use my_macro::MyTrait; |
| 184 | +extern crate proc_macro_examples; |
| 185 | +use proc_macro_examples::AnswerFn; |
194 | 186 |
|
195 |
| -#[derive(MyTrait)] |
196 |
| -struct Foo; |
| 187 | +#[derive(AnswerFn)] |
| 188 | +struct Struct; |
197 | 189 |
|
198 | 190 | fn main() {
|
199 |
| - drop(Foo); |
200 |
| - println!("the answer was: {}", answer()); |
| 191 | + assert_eq!(42, answer()); |
201 | 192 | }
|
202 | 193 | ```
|
203 | 194 |
|
204 |
| -and compiling it: |
| 195 | +#### Derive mode helper attributes |
205 | 196 |
|
206 |
| -``` |
207 |
| -the answer was: 2 |
208 |
| -``` |
209 |
| - |
210 |
| -Here we can see how the input to the macro was a `TokenStream` representing |
211 |
| -the three input tokens `struct Foo;`. While our output only contained the |
212 |
| -`answer` function, we were still able to use `Foo` in the main program because |
213 |
| -derive macros *append* items, they don't replace them. |
| 197 | +Derive mode macros can add additional [attributes] into the scope of the item |
| 198 | +they are on. Said attributes are called *derive mode helper attributes*. These |
| 199 | +attributes are inert, and their only purpose is to be fed into the derive |
| 200 | +mode macro that defined them. That said, they can be seen by all macros. |
214 | 201 |
|
215 |
| -Now this is a pretty wonky macro derive, and would likely be confusing to |
216 |
| -users! Derive macros are primarily geared towards implementing traits, like |
217 |
| -`Serialize` and `Deserialize`. |
| 202 | +The way to define helper attributes is to put an `attributes` key in the |
| 203 | +`proc_macro_derive` macro with a comma separated list of identifiers that are |
| 204 | +the names of the helper attributes. |
218 | 205 |
|
219 |
| -#### Derive helper attributes |
| 206 | +For example, the following derive mode macro defines a helper attribute |
| 207 | +`helper`, but ultimately doesn't do anything with it. |
220 | 208 |
|
221 |
| -An additional feature of derive macros is that they can whitelist names |
222 |
| -of attributes which are considered "helper attributes" and don't participate in |
223 |
| -normal attribute macro expansion. Taking our example from earlier we can |
224 |
| -define: |
| 209 | +```rust, ignore |
| 210 | +# extern crate proc_macro; |
| 211 | +# use proc_macro::TokenStream; |
225 | 212 |
|
226 |
| -```rust,ignore |
227 |
| -#[proc_macro_derive(MyTrait, attributes(my_attribute))] |
228 |
| -pub fn foo(item: TokenStream) -> TokenStream { |
229 |
| - // ... |
| 213 | +#[proc_macro_derive(HelperAttr, attributes(helper))] |
| 214 | +pub fn derive_helper_attr(_item: TokenStream) -> TokenStream { |
| 215 | + TokenStream::new(); |
230 | 216 | }
|
231 | 217 | ```
|
232 | 218 |
|
233 |
| -The extra `attributes` key in the `proc_macro_derive` attribute contains a |
234 |
| -comma-separated list of identifiers. Each identifier is a whitelist of |
235 |
| -an attribute name that can be attached to items which also have |
236 |
| -`#[derive(MyTrait)]`. These derive helper attributes will not do anything but |
237 |
| -will be passed through to the `foo` procedural macro defined above as part of |
238 |
| -the input. |
239 |
| - |
240 |
| -If we change our invocation to look like: |
| 219 | +And then usage on the derive mode on a struct: |
241 | 220 |
|
242 |
| -```rust,ignore |
243 |
| -#[derive(MyTrait)] |
244 |
| -#[my_attribute(hello)] |
245 |
| -struct Foo; |
246 | 221 | ```
|
| 222 | +# extern crate proc_macro_examples; |
| 223 | +# use proc_macro_examples::HelperAttr; |
247 | 224 |
|
248 |
| -you'll see that the `#[my_attribute(hello)]` attribute is fed through to the |
249 |
| -macro for processing. |
250 |
| - |
251 |
| -Attributes are often used to customize the behavior of derive macros. |
| 225 | +#[derive(HelperAttr)] |
| 226 | +struct Struct { |
| 227 | + #[helper] field: () |
| 228 | +} |
| 229 | +``` |
252 | 230 |
|
253 | 231 | ### Attribute macros
|
254 | 232 |
|
|
0 commit comments