Skip to content

Commit 30ab85e

Browse files
committed
Updates!
1 parent ef945a1 commit 30ab85e

File tree

1 file changed

+39
-16
lines changed

1 file changed

+39
-16
lines changed

posts/2018-12-21-Procedural-Macros-in-Rust-2018.md

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ three flavors:
2525
and bring all the goodness and ease of use of `#[derive(Debug)]` to
2626
user-defined traits as well, such as [Serde]'s `#[derive(Deserialize)]`.
2727

28-
* **Function-like macros** are new to the 2018 edition and allow defining
29-
macros like `env!("FOO")` or `format_args!("...")` in a crate.io-based
30-
library. You can think of these as sort of "`macro_rules!` macros" on
31-
steroids.
28+
* **Function-like macros** are newly stable to the 2018 edition and allow
29+
defining macros like `env!("FOO")` or `format_args!("...")` in a
30+
crate.io-based library. You can think of these as sort of "`macro_rules!`
31+
macros" on steroids.
3232

3333
* **Attribute macros**, my favorite, are also new in the 2018 edition
3434
and allow you to provide lightweight annotations on Rust functions which
@@ -87,7 +87,7 @@ Right off the bat we can see a few important properties of procedural macros:
8787
* We're *executing aribtrary code* at compile time, which means we can do just
8888
about anything!
8989
* Procedural macros are incorporated with the module system, meaning no more
90-
janky `#[macro_use]`
90+
they can be imported just like any other name.
9191

9292
Before we take a look at implementing a procedural macro, let's first dive into
9393
some of these points.
@@ -148,6 +148,12 @@ struct Foo {
148148
```
149149

150150
and you don't even need to explicitly depend on `serde_derive` in `Cargo.toml`!
151+
All you need is:
152+
153+
```toml
154+
[dependencies]
155+
serde = { version = '1.0.82', features = ['derive'] }
156+
```
151157

152158
### What's inside a `TokenStream`?
153159

@@ -156,9 +162,9 @@ This mysterious `TokenStream` type comes from the [compiler-provided
156162
[`TokenStream`] was call convert it to or from a string using `to_string()` or `parse()`.
157163
As of Rust 2018, you can act on the tokens in a [`TokenStream`] directly.
158164

159-
As its name might imply, a `TokenStream` can be thought of as a stream of
160-
tokens, or a list of tokens. Each token is called a [`TokenTree`] and represents
161-
one of four kinds of tokens:
165+
A [`TokenStream`] is effectively "just" an iterator over [`TokenTree`]. All
166+
syntax in Rust falls into one of these four categories, the four variants of
167+
[`TokenTree`]:
162168

163169
* `Ident` is any identifier like `foo` or `bar`. This also contains keywords
164170
such as `self` and `super`.
@@ -173,9 +179,6 @@ one of four kinds of tokens:
173179
delimited sub-token-stream. For example `(a, b)` is a `Group` with parentheses
174180
as delimiters, and the internal token stream is `a, b`.
175181

176-
All syntax in Rust falls into one of the above four categories. A `TokenStream`
177-
is effectively "just" an iterator over `TokenTree`.
178-
179182
While this is conceptually simple, this may sound like there's not much we can
180183
do with this! It's unclear, for example, how we might parse a function from a
181184
`TokenStream`. The minimality of `TokenTree` is crucial, however, for
@@ -251,11 +254,31 @@ together various pieces of syntax into one return value.
251254
### Tokens and `Span`
252255

253256
Perhaps the greatest feature of procedural macros in Rust 2018 is the ability to
254-
customize and use [`Span`] information on each token. A [`Span`] can be thought
255-
of as a pointer back into an original source file, typically saying something
256-
like "the `Ident` token` foo` came from file `bar.rs`, line 4, column 5, and was
257-
3 bytes long". This information is primarily used by the compiler's diagnostics
258-
with warnings and error messages.
257+
customize and use [`Span`] information on each token, giving us the ability for
258+
amazing syntactical error messages from procedural macros:
259+
260+
```
261+
error: expected `fn`
262+
--> src/main.rs:3:14
263+
|
264+
3 | my_annotate!(not_fn foo() {});
265+
| ^^^^^^
266+
```
267+
268+
as well as completely custom error messages:
269+
270+
```
271+
error: imported methods must have at least one argument
272+
--> invalid-imports.rs:12:5
273+
|
274+
12 | fn f1();
275+
| ^^^^^^^^
276+
```
277+
278+
A [`Span`] can be thought of as a pointer back into an original source file,
279+
typically saying something like "the `Ident` token` foo` came from file
280+
`bar.rs`, line 4, column 5, and was 3 bytes long". This information is
281+
primarily used by the compiler's diagnostics with warnings and error messages.
259282

260283
In Rust 2018 each [`TokenTree`] has a [`Span`] associated with it. This means that
261284
if you preserve the [`Span`] of all input tokens into the output then even

0 commit comments

Comments
 (0)