Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document custom derive. #38770

Merged
merged 1 commit into from
Jan 5, 2017
Merged

Conversation

steveklabnik
Copy link
Member

These are some bare-bones documentation for custom derive, needed
to stabilize "macros 1.1",
#35900

The book chapter is based off of a blog post by @cbreeden,
https://cbreeden.github.io/Macros11/

Normally, we have a policy of not mentioning external crates in
documentation. However, given that syn/quote are basically neccesary
for properly using macros 1.1, I feel that not including them here
would make the documentation very bad. So the rules should be bent
in this instance.

So far, this PR includes only docs; @alexcrichton said in #35900 that he'd be okay with landing them before stabilization; I don't mind either way.

@rust-highfive
Copy link
Collaborator

@steveklabnik: no appropriate reviewer found, use r? to override

@steveklabnik
Copy link
Member Author

/cc @rust-lang/docs @rust-lang/lang

@est31
Copy link
Member

est31 commented Jan 2, 2017

Would it make sense to document it in the reference as well? Derive is documented there too.

@steveklabnik
Copy link
Member Author

Would it make sense to document it in the reference as well? Derive is documented there too.

Ah ha! I knew I forgot something. Yes.

Copy link
Member

@killercup killercup left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Added a few inline comments with suggestions :)

With some kind of nice output, like `Hello, World! My name is Pancakes.`.

Let's go ahead and write up what we think our macro will look like from a user
perspective. In `hello-world\main.rs` we write:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't that be src/main.rs?

```

Great. So now we just need to actually write the procedural macro. Let's start
a new crate called `hello-world-macro` inside our `hello-world` project.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a sentence about naming convention? I've seen a bunch of crates that call these <name>_derive.

```

So there is a lot going on here. We have introduced two new crates: `syn` and
`quote`. As you may have noticed, `input: TokenSteam` is immediately converted
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Link "syn" and "quote" to their repositories or crates.io pages?

to a String. This String is a string representation of the rust code for which
we are deriving `HelloWorld` for--that's it. So what we really need is to be
able to _parse_ rust code into something usable. This is where `syn` comes to
play. `syn` is a `nom` based rust parser, and it's extremely useful. The
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

People who don't know nom will be confused by this.


So there is a lot going on here. We have introduced two new crates: `syn` and
`quote`. As you may have noticed, `input: TokenSteam` is immediately converted
to a String. This String is a string representation of the rust code for which
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lowercase "rust", same a few times below

}
```

So there is a lot going on here. We have introduced two new crates: `syn` and
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double space after sentence here and a few times below; I don't think the rest of the book does this.


So there is a lot going on here. We have introduced two new crates: `syn` and
`quote`. As you may have noticed, `input: TokenSteam` is immediately converted
to a String. This String is a string representation of the rust code for which
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put "String" in back ticks?

able to _parse_ rust code into something usable. This is where `syn` comes to
play. `syn` is a `nom` based rust parser, and it's extremely useful. The
other crate we've introduced is `quote`. It's essentially the dual of `syn` as
it will make serializing rust code really easy.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, maybe "serializing rust code" → "generating Rust code"?

let s = input.to_string();

// Parse the string representation
let ast = syn::parse_macro_input(&s).unwrap();
Copy link
Member

@killercup killercup Jan 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error handling probably deserves a short paragraph as well, even if it's just "panicking the proc macro code will show it as a compiler error pointing to the derive".

@withoutboats
Copy link
Contributor

How do you feel about making HelloWorld into a trait? Implementing traits is intended as the normal use case for derive, and it seems odd to document it with a non-trait impl (though of course that does work).

@steveklabnik
Copy link
Member Author

Okay! I think I've fixed up everything, @withoutboats @killercup . Thanks for the review 😄

Still have to update the reference, should push that in a minute.


```bash
error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type
--> hello-world-macro\src\lib.rs:8:3
--> hello-world-derive\src\lib.rs:8:3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI this path uses backslashes, the rest of the book probably not


* [Macros](book/macros.html) define new syntax in a higher-level,
declarative way.
* [Procedural Macros][procedural macros] can be used to implement custom derive.

## Macros
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little weird to me that ## Macros is a subheading within # Macros, but I see why you did it

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah i would like to re-do this and other parts of the reference but i'm trying to be minimally invasive here

@@ -2319,6 +2337,8 @@ impl<T: PartialEq> PartialEq for Foo<T> {
}
```

You can implement derive for your own type through [procedural macros](#procedural-macros).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Earlier in this section, "derive" is styled as

`derive`

with backticks, so might be good to be consistent.

* `--crate-type=proc-macro`, `#[crate_type = "proc-macro"]` - The output
produced is not specified, but if a `-L` path is provided to it then the
compiler will recognize the output artifacts as a macro and it can be loaded
for a program. If a crate is compiled with the proc-macro crate type t will
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/t will/it will/

It looks like crate types are styled with backticks, so also:

s/proc-macro/`proc-macro`/

forbid exporting any items in the crate other than those functions tagged
`#[proc_macro_derive]` and those functions must also be placed at the crate
root. Finally, the compiler will automatically set the `cfg(proc_macro)`
annotation whenever any crate type of a compilation is the rustc-macro crate
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/rustc-macro/`rustc-macro`/

Copy link
Contributor

@nikomatsakis nikomatsakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seemed like a decent summary. The one thing I would say is that I would have expected a bit more up-front material discussing the 'basic model' for derive (string in, string out, you do some parsing and serialization in the model), but that's because I tend towards more of a "top-down" style, I think, whereas this is kind of "bottom-up". (i.e., here is the code to write, let's explain why you wrote it) In any case, all the important stuff seems to be there, and it reads pretty smoothly.

I left some nits.


Rust includes several traits that you can derive, but it also lets you define
your own. We can accomplish this task through a feature of Rust called
"procedrual macros." Eventually, procedural macros will allow for all sorts of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: procedural

type using `ast.ident`. The `quote!` macro let's us write up the Rust code
that we wish to return and convert it into `Tokens`. `quote!` let's us use some
really cool templating mechanics; we simply write `#name` and `quote!` will
replace it with the variable named `name`. You can even do some repition
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: s/repition/repetition/

@@ -652,6 +648,28 @@ Rust syntax is restricted in two ways:

[RFC 550]: https://github.com/rust-lang/rfcs/blob/master/text/0550-macro-future-proofing.md

## Procedrual Macros
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: s/Procedrual/Procedural/

@@ -0,0 +1,209 @@
% Procedrual Macros (and custom Derive)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: s/Procedrual/Procedural/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i am... really bad at this spelling apparently, haha

```

Oh, so it appears that we need to declare that our `hello-world-derive` crate is
a `proc-macro` crate type. How do we do this? Like this:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This stream of consciousness bit felt a little over the top to me, but it's fine. =)

@steveklabnik
Copy link
Member Author

The one thing I would say is that I would have expected a bit more up-front material discussing the 'basic model' for derive

I'm not totally opposed to this; I was thinking about making it future proof. That is, right now it's string in string out because it's a hack. It's really TokenStream -> String -> process -> String -> TokenStream, and ideally those string bits are going away. But I guess it will be a long time...

@alexcrichton alexcrichton added the beta-nominated Nominated for backporting to the compiler in the beta channel. label Jan 2, 2017
@@ -52,6 +52,7 @@
* [Borrow and AsRef](borrow-and-asref.md)
* [Release Channels](release-channels.md)
* [Using Rust without the standard library](using-rust-without-the-standard-library.md)
* [Procedural Macros (and custom Derive)](procedural-macros.md)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see we’re continuing the tradition of inconsistent capitalisation—nay, taking it to a new level, with mixed title and sentence case all in one line!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah "Derive" felt like something that deserved a capital here, maybe not, idk

@F001
Copy link
Contributor

F001 commented Jan 3, 2017

Thanks for writing the doc. I can get the basic concept of proc-macro now, but still haunted by some questions.

  1. Is it possible to write this macro without any third party dependency? What if we create it from scratch? What kind of benefit we can get from syn&quote crate?
  2. Why does TokenStream type exist, if the only purpose of it is to be converted to a String? It is weird to convert a structured type to a String, and then parse. I would like to see more explanation about what kind of API does the proc-macro feature provide for us, not the API of a third party lib.


A number of minor features of Rust are not central enough to have their own
syntax, and yet are not implementable as functions. Instead, they are given
names, and invoked through a consistent syntax: `some_extension!(...)`.

Users of `rustc` can define new syntax extensions in two ways:

* [Compiler plugins][plugin] can include arbitrary Rust code that
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably continue to document compiler plugins in the reference for the time being; they still exist & will for some time, and of course some project are heavily reliant on them.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't removing documentation; it's re-ordering it. I thought putting macros by example first was a better way of doing things.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like it went from Compiler plugins, Macros to Macros, Procedural Macros, shouldn't it be Macros, Procedural Macros, Compiler plugins?

Copy link
Member Author

@steveklabnik steveklabnik Jan 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that procedural macros are compiler plugins. That is, today "compiler plugins" are "using libsyntax to extend the compiler" but that's never going to be stable, only the new interface we're calling "procedural macros"

The names of all of this stuff has been very hard.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's correct, but we've implemented (and now stabilized) the new procedural macros interface only for controlling derive macros. Users who want to define custom attributes or function style procedural macros (without using a hack) are still using the never-to-be-stabilized compiler plugin interface.

Until we've fully implemented procedural macros, compiler plugins will still be used by these users and so we shouldn't drop whatever docs we have on it (even if we don't focus any attention on improving it).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't actually remove any of those docs generally, just the small description here. Let me fix it.

```

To make sure that our `hello-world` crate is able to find this new crate we've
crated, we'll add it to our toml:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/crated/created/

@steveklabnik
Copy link
Member Author

@F001

Is it possible to write this macro without any third party dependency? What if we create it from scratch? What kind of benefit we can get from syn&quote crate?

Sure, but it's much, much, much harder. The benefits are that syn and quote do parsing and generation for you.

Why does TokenStream type exist, if the only purpose of it is to be converted to a String? It is weird to convert a structured type to a String, and then parse.

This is the difference between macros 1.1 and macros 2.0, in a nutshell.

I would like to see more explanation about what kind of API does the proc-macro feature provide for us, not the API of a third party lib.

This is the entire API. That's it. There's nothing else to explain. It is extremely bare-bones.

@steveklabnik
Copy link
Member Author

Okay, I believe that I've addressed everyone's comments now, both on GitHub and in the text. I've squashed, since this is going to need to be backported, and want to make that easy.

@nikomatsakis
Copy link
Contributor

Let's do it.

@bors r+

@bors
Copy link
Contributor

bors commented Jan 3, 2017

📌 Commit eb2c83f has been approved by nikomatsakis

@nikomatsakis
Copy link
Contributor

@bors rollup

@nikomatsakis nikomatsakis added the beta-accepted Accepted for backporting to the compiler in the beta channel. label Jan 3, 2017
@steveklabnik
Copy link
Member Author

@bors: r=nikomatsakis rollup (#38770 (comment))

@bors
Copy link
Contributor

bors commented Jan 3, 2017

📌 Commit c0efdbf has been approved by nikomatsakis

GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this pull request Jan 4, 2017
…ikomatsakis

Document custom derive.

These are some bare-bones documentation for custom derive, needed
to stabilize "macros 1.1",
rust-lang#35900

The book chapter is based off of a blog post by @cbreeden,
https://cbreeden.github.io/Macros11/

Normally, we have a policy of not mentioning external crates in
documentation. However, given that syn/quote are basically neccesary
for properly using macros 1.1, I feel that not including them here
would make the documentation very bad. So the rules should be bent
in this instance.

So far, this PR includes only docs; @alexcrichton said in rust-lang#35900 that he'd be okay with landing them before stabilization; I don't mind either way.
@alexcrichton
Copy link
Member

@bors: rollup- p=1

Let's land this quickly so we can backport

@bors
Copy link
Contributor

bors commented Jan 4, 2017

🔒 Merge conflict

@bors
Copy link
Contributor

bors commented Jan 4, 2017

☔ The latest upstream changes (presumably #38783) made this pull request unmergeable. Please resolve the merge conflicts.

These are some bare-bones documentation for custom derive, needed
to stabilize "macros 1.1",
rust-lang#35900

The book chapter is based off of a blog post by @cbreeden,
https://cbreeden.github.io/Macros11/

Normally, we have a policy of not mentioning external crates in
documentation. However, given that syn/quote are basically neccesary
for properly using macros 1.1, I feel that not including them here
would make the documentation very bad. So the rules should be bent
in this instance.
@steveklabnik
Copy link
Member Author

@bors: r=alexcrichton p=1

@bors
Copy link
Contributor

bors commented Jan 4, 2017

📌 Commit 3075c1f has been approved by alexcrichton

moment, procedural macros need to be in their own crate. Eventually, this
restriction may be lifted, but for now, it's required. As such, there's a
convention; for a crate named `foo`, a custom derive procedural macro is called
`foo-derive`. Let's start a new crate called `hello-world-derive` inside our
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the convention is foo_derive not foo-derive.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

package names are supposed to prefer - over _.

@bors
Copy link
Contributor

bors commented Jan 4, 2017

⌛ Testing commit 3075c1f with merge 8b9bc29...

@bors
Copy link
Contributor

bors commented Jan 4, 2017

💔 Test failed - status-travis

@steveklabnik
Copy link
Member Author

@bors: retry

(travis is spurious)

@steveklabnik
Copy link
Member Author

@bors: force

@bors
Copy link
Contributor

bors commented Jan 5, 2017

⌛ Testing commit 3075c1f with merge 5d994d8...

bors added a commit that referenced this pull request Jan 5, 2017
Document custom derive.

These are some bare-bones documentation for custom derive, needed
to stabilize "macros 1.1",
#35900

The book chapter is based off of a blog post by @cbreeden,
https://cbreeden.github.io/Macros11/

Normally, we have a policy of not mentioning external crates in
documentation. However, given that syn/quote are basically neccesary
for properly using macros 1.1, I feel that not including them here
would make the documentation very bad. So the rules should be bent
in this instance.

So far, this PR includes only docs; @alexcrichton said in #35900 that he'd be okay with landing them before stabilization; I don't mind either way.
@bors
Copy link
Contributor

bors commented Jan 5, 2017

☀️ Test successful - status-appveyor, status-travis
Approved by: alexcrichton
Pushing 5d994d8 to master...

@bors bors merged commit 3075c1f into rust-lang:master Jan 5, 2017
@nikomatsakis nikomatsakis mentioned this pull request Jan 6, 2017
17 tasks
@alexcrichton alexcrichton removed the beta-nominated Nominated for backporting to the compiler in the beta channel. label Jan 6, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
beta-accepted Accepted for backporting to the compiler in the beta channel.
Projects
None yet
Development

Successfully merging this pull request may close these issues.