Skip to content

Commit a1ccd19

Browse files
committed
Derive mode macros
1 parent 5d43c04 commit a1ccd19

File tree

1 file changed

+50
-72
lines changed

1 file changed

+50
-72
lines changed

src/procedural-macros.md

Lines changed: 50 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
*Procedural macros* allow creating syntax extensions as execution of a function.
44
Procedural macros come in one of three flavors:
55

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]`
99

1010
Procedural macros allow you to run code at compile time that operates over Rust
1111
syntax, both consuming and producing Rust syntax. You can sort of think of
@@ -150,105 +150,83 @@ pub fn foo(_input: TokenStream) -> TokenStream {
150150
the answer was: 4
151151
```
152152

153-
### Derive macros
153+
### Derive mode macros
154154

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.
158158

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`].
160162

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.
171167

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`.
176170

177171
```rust,ignore
178172
extern crate proc_macro;
179-
use proc_macro::*;
173+
use proc_macro::TokenStream;
180174
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()
185178
}
186179
```
187180

188-
using it liek:
181+
And then using said derive mode:
189182

190183
```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;
194186
195-
#[derive(MyTrait)]
196-
struct Foo;
187+
#[derive(AnswerFn)]
188+
struct Struct;
197189
198190
fn main() {
199-
drop(Foo);
200-
println!("the answer was: {}", answer());
191+
assert_eq!(42, answer());
201192
}
202193
```
203194

204-
and compiling it:
195+
#### Derive mode helper attributes
205196

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.
214201

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.
218205

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.
220208

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;
225212
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();
230216
}
231217
```
232218

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:
241220

242-
```rust,ignore
243-
#[derive(MyTrait)]
244-
#[my_attribute(hello)]
245-
struct Foo;
246221
```
222+
# extern crate proc_macro_examples;
223+
# use proc_macro_examples::HelperAttr;
247224
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+
```
252230

253231
### Attribute macros
254232

0 commit comments

Comments
 (0)