Skip to content

Commit 846670c

Browse files
committed
First pass removal of guide-level material
1 parent b865eb7 commit 846670c

File tree

1 file changed

+22
-158
lines changed

1 file changed

+22
-158
lines changed

src/procedural-macros.md

Lines changed: 22 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -39,30 +39,17 @@ pub fn foo() {}
3939
4040
because the `foo` function is not a procedural macro. Procedural macros are
4141
loaded dynamically by the compiler when they are needed during compilation.
42-
Cargo will naturally make procedural macro crates available to crates which
43-
depend on them, or you can use the `--extern` argument.
4442

4543
### The `proc_macro` crate
4644

47-
Procedural macro crates almost always will link to the in-tree `proc_macro`
48-
crate. The `proc_macro` crate is a compiler-provided crate which provides
45+
Procedural macro crates almost always will link to the compiler-provided
46+
`proc_macro` crate. The `proc_macro` crate is a crate which provides
4947
facilities to working with the types of each procedural macro function. You can
50-
learn more about this crate by exploring [the documentation][pm-dox].
51-
52-
[pm-dox]: https://doc.rust-lang.org/stable/proc_macro/
53-
54-
Linking to the `proc_macro` crate can currently be done with:
55-
56-
```rust,ignore
57-
extern crate proc_macro;
58-
```
59-
60-
In the 2018 edition, however, this statement will not be necessary.
48+
learn more about this crate by exploring [its documentation][proc macro crate].
6149

6250
### The `TokenStream` Type
6351

64-
One aspect you may notice about the `proc_macro` crate is that it doesn't
65-
contain any AST items! Instead, it primarily contains a `TokenStream` type.
52+
It primarily contains a `TokenStream` type.
6653
Procedural macros operate over *token streams* instead of AST nodes,
6754
which is a far more stable interface over time for both the compiler and for
6855
procedural macros to target.
@@ -72,13 +59,9 @@ can roughly be thought of as lexical token. For example `foo` is an `Ident`
7259
token, `.` is a `Punct` token, and `1.2` is a `Literal` token. The `TokenStream`
7360
type, unlike `Vec<TokenTree>`, is cheap to clone (like `Rc<T>`).
7461

75-
To learn more about token streams, let's first dive into writing our first
76-
procedural macro.
77-
7862
### Bang Macros
7963

80-
The first kind of procedural macro is the "procedural bang macro" macro. This
81-
flavor of procedural macro is like writing `macro_rules!` only you get to
64+
This flavor of procedural macro is like writing `macro_rules!` only you get to
8265
execute arbitrary code over the input tokens instead of being limited to
8366
`macro_rules!` syntax.
8467

@@ -94,22 +77,7 @@ pub fn foo(input: TokenStream) -> TokenStream {
9477

9578
This item is defining a procedural bang macro (`#[proc_macro]`) which is called
9679
`foo`. The first argument is the input to the macro which explore in a second,
97-
and the return value is the tokens that it should expand to. Let's fill in all
98-
the pieces here with a noop macro.
99-
100-
First up, let's generate a skeleton project:
101-
102-
```sh
103-
$ cargo new foo
104-
$ cd foo
105-
$ cargo new my-macro --lib
106-
$ echo 'my-macro = { path = "my-macro" }' >> Cargo.toml
107-
$ echo '[lib]' >> my-macro/Cargo.toml
108-
$ echo 'proc-macro = true' >> my-macro/Cargo.toml
109-
```
110-
111-
This'll set up a main binary project called `foo` along with a subcrate called
112-
`my-macro` which is declared as a procedural macro. Next up we'll fill in:
80+
and the return value is the tokens that it should expand to.
11381

11482
```rust,ignore
11583
// my-macro/src/lib.rs
@@ -136,20 +104,6 @@ fn main() {
136104
}
137105
```
138106

139-
and finally, build it!
140-
141-
```sh
142-
$ cargo run
143-
Compiling my-macro v0.1.0 (file://.../foo/my-macro)
144-
Compiling foo v0.1.0 (file://.../foo)
145-
Finished dev [unoptimized + debuginfo] target(s) in 0.37s
146-
Running `target/debug/foo`
147-
the answer was: 3
148-
```
149-
150-
Alright! This end-to-end example shows how we can create a macro that doesn't
151-
do anything, so let's do something a bit more useful.
152-
153107
First up, let's see what the input to our macro looks like by modifying our
154108
macro:
155109

@@ -162,39 +116,7 @@ pub fn foo(input: TokenStream) -> TokenStream {
162116
}
163117
```
164118

165-
and reexecute (output edited slightly here):
166-
167-
```sh
168-
$ cargo run
169-
Compiling my-macro v0.1.0 (file://.../foo/my-macro)
170-
Compiling foo v0.1.0 (file://.../foo)
171-
TokenStream [
172-
Ident { ident: "fn", span: #0 bytes(39..41) },
173-
Ident { ident: "answer", span: #0 bytes(42..48) },
174-
Group { delimiter: Parenthesis, stream: TokenStream [], span: #0 bytes(48..50) },
175-
Punct { ch: '-', spacing: Joint, span: #0 bytes(51..53) },
176-
Punct { ch: '>', spacing: Alone, span: #0 bytes(51..53) },
177-
Ident { ident: "u32", span: #0 bytes(54..57) },
178-
Group {
179-
delimiter: Brace,
180-
stream: TokenStream [
181-
Literal { lit: Integer(3), suffix: None, span: #0 bytes(60..61) }
182-
],
183-
span: #0 bytes(58..63)
184-
}
185-
]
186-
Finished dev [unoptimized + debuginfo] target(s) in 0.37s
187-
Running `target/debug/foo`
188-
the answer was: 3
189-
```
190-
191-
Here we can see how a procedural bang macro's input is a token stream (list) of
192-
all the tokens provided as input to the macro itself, excluding the delimiters
193-
used to invoke the macro. Notice how the braces and parentheses are using the
194-
`Group` token tree which is used to enforce that macros always have balanced
195-
delimiters.
196-
197-
As you may have guessed by now the macro invocation is effectively replaced by
119+
The macro invocation is effectively replaced by
198120
the return value of the macro, creating the function that we provided as input.
199121
We can see another example of this where we simply ignore the input:
200122

@@ -206,26 +128,15 @@ pub fn foo(_input: TokenStream) -> TokenStream {
206128
}
207129
```
208130

209-
and recompiling shows:
210-
211-
```sh
212-
$ cargo run
213-
Compiling my-macro v0.1.0 (file://.../foo/my-macro)
214-
Compiling foo v0.1.0 (file://.../foo)
215-
Finished dev [unoptimized + debuginfo] target(s) in 0.37s
216-
Running `target/debug/foo`
131+
```
217132
the answer was: 4
218133
```
219134

220-
showing us how the input was ignored and the macro's output was used instead.
221-
222135
### Derive macros
223136

224-
[The book][procedural macros] has a tutorial on creating derive macros and here
225-
we'll go into some of the nitty-gritty of how this works. The derive macro
226-
feature allows you to define a new `#[derive(Foo)]` mode which often makes it
227-
much easier to systematically implement traits, removing quite a lot of
228-
boilerplate.
137+
The derive macro feature allows you to define a new `#[derive(Foo)]` mode which
138+
often makes it much easier to systematically implement traits, removing quite a
139+
lot of boilerplate.
229140

230141
Custom derives are defined like so:
231142

@@ -274,17 +185,7 @@ fn main() {
274185

275186
and compiling it:
276187

277-
```sh
278-
$ cargo run
279-
Compiling my-macro v0.1.0 (file://.../foo/my-macro)
280-
Compiling foo v0.1.0 (file://.../foo)
281-
TokenStream [
282-
Ident { ident: "struct", span: #0 bytes(67..73) },
283-
Ident { ident: "Foo", span: #0 bytes(74..77) },
284-
Punct { ch: ';', spacing: Alone, span: #0 bytes(77..78) }
285-
]
286-
Finished dev [unoptimized + debuginfo] target(s) in 0.34s
287-
Running `target/debug/foo`
188+
```
288189
the answer was: 2
289190
```
290191

@@ -295,11 +196,7 @@ derive macros *append* items, they don't replace them.
295196

296197
Now this is a pretty wonky macro derive, and would likely be confusing to
297198
users! Derive macros are primarily geared towards implementing traits, like
298-
`Serialize` and `Deserialize`. The `syn` crate also has a [number of
299-
examples][synex] of defining derive macros.
300-
301-
[procedural macros]: ../book/first-edition/procedural-macros.html
302-
[synex]: https://github.com/dtolnay/syn/tree/master/examples
199+
`Serialize` and `Deserialize`.
303200

304201
#### Derive helper attributes
305202

@@ -333,14 +230,13 @@ struct Foo;
333230
you'll see that the `#[my_attribute(hello)]` attribute is fed through to the
334231
macro for processing.
335232

336-
Attributes are often used to customize the behavior of derive macros, such as
337-
the `#[serde]` attribute for the `serde` crate.
233+
Attributes are often used to customize the behavior of derive macros.
338234

339235
### Attribute macros
340236

341-
The third and final form of procedural macros is the attribute macro. Attribute
342-
macros allow you to define a new `#[attr]`-style attribute which can be
343-
attached to items and generate wrappers and such. These macros are defined like:
237+
Attribute macros allow you to define a new `#[attr]`-style attribute which can
238+
be attached to items and generate wrappers and such. These macros are defined
239+
like:
344240

345241
```rust,ignore
346242
#[proc_macro_attribute]
@@ -353,9 +249,8 @@ The `#[proc_macro_attribute]` indicates that this macro is an attribute macro
353249
and can only be invoked like `#[foo]`. The name of the function here will be the
354250
name of the attribute as well.
355251

356-
The first input, `attr`, is the arguments to the attribute provided, which
357-
we'll see in a moment. The second argument, `item`, is the item that the
358-
attribute is attached to.
252+
The first input, `attr`, is the arguments to the attribute provided. The second
253+
argument, `item`, is the item that the attribute is attached to.
359254

360255
Like with bang macros at the beginning (and unlike derive macros), the return
361256
value here *replaces* the input `item`.
@@ -402,9 +297,7 @@ fn main() {
402297

403298
compiled as:
404299

405-
```sh
406-
$ cargo run
407-
Compiling foo v0.1.0 (file://.../foo)
300+
```
408301
attr:
409302
input: fn invoke1() { }
410303
attr: bar
@@ -413,8 +306,6 @@ attr: crazy custom syntax
413306
input: fn invoke3() { }
414307
attr: delimiters
415308
input: fn invoke4() { }
416-
Finished dev [unoptimized + debuginfo] target(s) in 0.12s
417-
Running `target/debug/foo`
418309
```
419310

420311
Here we can see how the arguments to the attribute show up in the `attr`
@@ -452,31 +343,6 @@ pub fn swap_spans(input: TokenStream) -> TokenStream {
452343
}
453344
```
454345

455-
We can see what's going on here by feeding invalid syntax into the macro and
456-
seeing what the compiler reports. Let's start off by seeing what the compiler
457-
does normally:
458-
459-
```rust,ignore
460-
// src/main.rs
461-
fn _() {}
462-
```
463-
464-
is compiled as:
465-
466-
```sh
467-
$ cargo run
468-
Compiling foo v0.1.0 (file://.../foo)
469-
error: expected identifier, found reserved identifier `_`
470-
--> src/main.rs:1:4
471-
|
472-
1 | fn _() {}
473-
| ^ expected identifier, found reserved identifier
474-
475-
error: aborting due to previous error
476-
```
477-
478-
but when we feed it through our macro:
479-
480346
```rust,ignore
481347
extern crate my_macro;
482348
@@ -505,9 +371,6 @@ notice how the error message is pointing to the wrong span! This is because we
505371
swapped the spans of the first two tokens, giving the compiler false information
506372
about where the tokens came from.
507373

508-
Controlling spans is quite a powerful feature and needs to be used with care,
509-
misuse can lead to some excessively confusing error messages!
510-
511374
### Procedural macros and hygiene
512375

513376
Currently all procedural macros are "unhygienic". This means that all procedural
@@ -551,4 +414,5 @@ macros in some respects. These limitations include:
551414
exists on stable your only option is to `panic!` or to in some cases expand to
552415
an invocation of the `compile_error!` macro with a custom message.
553416

554-
[crate type]: linkage.html
417+
[crate type]: linkage.html
418+
[proc_macro crate]: ../proc_macro/index.html

0 commit comments

Comments
 (0)