Remove the 2018 edition nostarch directory

I'm not using this anymore and it's just confusing people
This commit is contained in:
Carol (Nichols || Goulding) 2019-03-01 16:54:17 -05:00
parent fab9419503
commit 8b3ad7fef3
No known key found for this signature in database
GPG Key ID: D04B39A6CA243902
3 changed files with 0 additions and 1864 deletions

View File

@ -1,245 +0,0 @@
# Appendix D - Useful Development Tools
In this appendix, well talk about tools provided by the Rust project that are
useful when developing Rust code.
## Automatic Formatting with `rustfmt`
The tool `rustfmt` reformats your code according to the community code style.
Many projects use `rustfmt` to prevent arguments about which style to use when
writing Rust: everyone formats their code with the tool!
The `rustfmt` tool is not yet at the quality of a version 1.0 release, but
a preview is available for you to use in the meantime. Please give it a try and
let us know how it goes!
To install `rustfmt`:
```
$ rustup component add rustfmt-preview
```
This will give you both `rustfmt` and `cargo-fmt`, similar to how Rust gives
you both `rustc` and `cargo`. To take any Cargo project and format it:
```
$ cargo fmt
```
Running this command will reformat all of the Rust code in the current crate.
This should only change the code style, not the code semantics. For more
information on `rustfmt`, see its documentation at
*https://github.com/rust-lang-nursery/rustfmt*.
## Fix Up Your Code with `rustfix`
If youve written code in Rust, youve probably seen compiler warnings. For
example, consider this code:
Filename: src/main.rs
```
fn do_something() {}
fn main() {
for i in 0..100 {
do_something();
}
}
```
Here, were calling the `do_something` function 100 times, but we never use the
variable `i` in the body of the `for` loop. Rust warns us about that:
```
$ cargo build
Compiling myprogram v0.1.0 (file:///projects/myprogram)
warning: unused variable: `i`
--> src/main.rs:4:9
|
4 | for i in 1..100 {
| ^ help: consider using `_i` instead
|
= note: #[warn(unused_variables)] on by default
Finished dev [unoptimized + debuginfo] target(s) in 0.50s
```
The warning suggests that we use `_i` as a name instead: the underscore
indicates that we intend for this variable to be unused. We can automatically
apply that suggestion using the `rustfix` tool by running the command `cargo
fix`:
```
$ cargo fix
Checking myprogram v0.1.0 (file:///projects/myprogram)
Fixing src/main.rs (1 fix)
Finished dev [unoptimized + debuginfo] target(s) in 0.59s
```
If we look at *src/main.rs* again, well see that `cargo fix` has changed the
code:
Filename: src/main.rs
```
fn do_something() {}
fn main() {
for _i in 0..100 {
do_something();
}
}
```
The `for` loop variable is now named `_i`, and the warning will no longer
appear.
The `cargo fix` command can also be used to transition your code between
different editions of Rust. Editions are covered in Appendix E.
## More Lints with `clippy`
The `clippy` tool is a collection of lints to catch common mistakes and improve
your Rust code.
The `clippy` tool is not yet at the quality of a version 1.0 release, but a
preview is available for you to use in the meantime. Please give it a try and
let us know how it goes!
To install `clippy`:
```
$ rustup component add clippy-preview
```
To take any Cargo project and run clippys lints on it:
```
$ cargo clippy
```
For example, if you write a program that uses an approximation of a
mathematical constant such as pi, as this program does:
Filename: src/main.rs
```
fn main() {
let x = 3.1415;
let r = 8.0;
println!("the area of the circle is {}", x * r * r);
}
```
Running `cargo clippy` on this project will result in this error:
```
error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
--> src/main.rs:2:13
|
2 | let x = 3.1415;
| ^^^^^^
|
= note: #[deny(clippy::approx_constant)] on by default
= help: for further information visit https://rust-lang-nursery.github.io/rust-clippy/v0.0.212/index.html#approx_constant
```
This lets you know that Rust has this constant defined more precisely, and that
your program would be more correct if you used the constant instead. This code
doesnt result in any errors or warnings from `clippy`:
Filename: src/main.rs
```
fn main() {
let x = std::f64::consts::PI;
let r = 8.0;
println!("the area of the circle is {}", x * r * r);
}
```
For more information on `clippy`, see its documentation at
*https://github.com/rust-lang-nursery/rust-clippy*.
## IDE Integration Using the Rust Language Server
To help IDE integration, the Rust project distributes the `rls`, which stands
for the Rust Language Server. This tool speaks the Language Server Protocol
described at *http://langserver.org/*, which is a specification for IDEs and
programming languages to communicate with each other. The `rls` can be used by
different clients, such as the Rust plugin for Visual Studio: Code at
*https://marketplace.visualstudio.com/items?itemName=rust-lang.rust*.
The `rls` is not yet at the quality of a version 1.0 release, but a preview is
available for you to use in the meantime. Please give it a try and let us know
how it goes!
To install the `rls`:
```
$ rustup component add rls-preview
```
Then install the language server support in your particular IDE, and you will
gain abilities such as autocompletion, jump to definition, and inline errors.
For more information on the `rls`, see its documentation at
*https://github.com/rust-lang-nursery/rls*.
# Appendix E - Editions
Way back in Chapter 1, we saw that `cargo new` adds a bit of metadata to your
*Cargo.toml* about an `edition`. This appendix talks about what that means!
The Rust language and compiler have a six-week release cycle. This means users
get a constant stream of new features. Other programming languages release
larger changes less often; Rust chooses to release smaller updates more
frequently. After a while, all of those tiny changes add up. But from release
to release, it can be hard to look back and say “Wow, between Rust 1.10 and
Rust 1.31, Rust has changed a lot!”
Every two or three years, the Rust team produces a new *edition* of Rust.
Each edition brings together the features that have landed into a clear
package with fully updated documentation and tooling. New editions ship
as part of the usual six-week release process.
This serves different purposes for different people:
* For active Rust users, it brings together incremental changes into an
easy-to-understand package.
* For non-users, it signals that some major advancements have landed, which
might make Rust worth another look.
* For those developing Rust itself, it provides a rallying point for the
project as a whole.
At the time of writing, there are two editions: Rust 2015 and Rust 2018.
This book is written using Rust 2018 edition idioms.
The `edition` key in *Cargo.toml* indicates which edition your code should be
compiled under. If the key does not exist, it defaults to `2015` for backwards
compatibility reasons.
Each project can choose to opt in to an edition other than the default 2015
edition. By doing so, editions can contain incompatible changes, such as adding
a new keyword that might conflict with identifiers in code or turning warnings
into errors. But unless you opt in to those changes, your code will continue to
compile even as you upgrade the version of the Rust compiler that you use. All
Rust compiler versions support any edition that existed prior to that
compilers release, and they can link crates of any supported editions
together. Edition changes only affect the way the compiler initially parses
code. Therefore, if youre using Rust 2015 and one of your dependencies uses
Rust 2018, your project will compile and be able to use that dependency. The
opposite situation, where your project uses Rust 2018 and a dependency uses
Rust 2015, works as well.
To be clear: most features will be available on all editions. Developers using
any edition of Rust will continue to see improvements as new stable releases
are made. In some cases, however, mainly when new keywords are added, there may
be new features that are only available in later editions. You only need to
switch editions if you want to take advantage of such features.
For more details, the Edition
Guide at *https://rust-lang-nursery.github.io/edition-guide/* is a complete
book about editions, including how to automatically upgrade your code to
a new edition via `cargo fix`.

File diff suppressed because it is too large Load Diff

View File

@ -1,595 +0,0 @@
<!-- This paragraph is the same as the one on page 448; I'm including it here
to show where the new content should go. /Carol -->
This code will compile just fine. For more about trait objects, refer to the
“Using Trait Objects That Allow for Values of Different Types” section in
Chapter 17.
<!-- This is the start of the new content on macros, some of which used to be
in Appendix D. /Carol -->
Next, lets look at macros!
## Macros
<!-- This intro is new. /Carol -->
Weve used macros like `println!` throughout this book, but we havent fully
explored what a macro is and how it works. *Macros* refers to a family of
features in Rust:
* *Declarative* macros with `macro_rules!`
* *Procedural* macros, which come in three kinds:
* Custom `#[derive]` macros
* Attribute-like macros
* Function-like macros
Well talk about each of these in turn, but first, why do we even need macros
when we already have functions?
### The Difference Between Macros and Functions
<!-- This section is largely the same as it appears in Appendix D; I can
provide a list of the small changes if that would be helpful. /Carol -->
Fundamentally, macros are a way of writing code that writes other code, which
is known as *metaprogramming*. In Appendix C, we discuss the `derive`
attribute, which generates an implementation of various traits for you. Weve
also used the `println!` and `vec!` macros throughout the book. All of these
macros *expand* to produce more code than the code youve written manually.
Metaprogramming is useful for reducing the amount of code you have to write and
maintain, which is also one of the roles of functions. However, macros have
some additional powers that functions dont have.
A function signature must declare the number and type of parameters the
function has. Macros, on the other hand, can take a variable number of
parameters: we can call `println!("hello")` with one argument or
`println!("hello {}", name)` with two arguments. Also, macros are expanded
before the compiler interprets the meaning of the code, so a macro can, for
example, implement a trait on a given type. A function cant, because it gets
called at runtime and a trait needs to be implemented at compile time.
The downside to implementing a macro instead of a function is that macro
definitions are more complex than function definitions because youre writing
Rust code that writes Rust code. Due to this indirection, macro definitions are
generally more difficult to read, understand, and maintain than function
definitions.
There is one last important difference between macros and functions: you must
define or bring macros into scope *before* you call them in a file, whereas you
can define functions anywhere and call them anywhere.
### Declarative Macros with `macro_rules!` for General Metaprogramming
<!-- This section is largely the same as it appears in Appendix D; I can
provide a list of the small changes if that would be helpful. /Carol -->
The most widely used form of macros in Rust are *declarative macros*. These are
also sometimes referred to as “macros by example”, “`macro_rules!` macros”, or
just plain “macros”. At their core, declarative macros allow you to write
something similar to a Rust `match` expression. As discussed in Chapter 6,
`match` expressions are control structures that take an expression, compare the
resulting value of the expression to patterns, and then run the code associated
with the matching pattern. Macros also compare a value to patterns that have
code associated with them; in this situation, the value is the literal Rust
source code passed to the macro, the patterns are compared with the structure
of that source code, and the code associated with each pattern is the code that
replaces the code passed to the macro. This all happens during compilation.
To define a macro, you use the `macro_rules!` construct. Lets explore how to
use `macro_rules!` by looking at how the `vec!` macro is defined. Chapter 8
covered how we can use the `vec!` macro to create a new vector with particular
values. For example, the following macro creates a new vector with three
integers inside:
```
let v: Vec<u32> = vec![1, 2, 3];
```
We could also use the `vec!` macro to make a vector of two integers or a vector
of five string slices. We wouldnt be able to use a function to do the same
because we wouldnt know the number or type of values up front.
Lets look at a slightly simplified definition of the `vec!` macro in Listing
19-36.
Filename: src/lib.rs
```
#[macro_export]
macro_rules! vec {
( $( $x:expr ),* ) => {
{
let mut temp_vec = Vec::new();
$(
temp_vec.push($x);
)*
temp_vec
}
};
}
```
Listing 19-36: A simplified version of the `vec!` macro definition
> Note: The actual definition of the `vec!` macro in the standard library
> includes code to preallocate the correct amount of memory up front. That code
> is an optimization that we dont include here to make the example simpler.
The `#[macro_export]` annotation indicates that this macro should be made
available whenever the crate in which were defining the macro is brought into
scope. Without this annotation, the macro cant be brought into scope.
We then start the macro definition with `macro_rules!` and the name of the
macro were defining *without* the exclamation mark. The name, in this case
`vec`, is followed by curly brackets denoting the body of the macro definition.
The structure in the `vec!` body is similar to the structure of a `match`
expression. Here we have one arm with the pattern `( $( $x:expr ),* )`,
followed by `=>` and the block of code associated with this pattern. If the
pattern matches, the associated block of code will be emitted. Given that this
is the only pattern in this macro, there is only one valid way to match; any
other will be an error. More complex macros will have more than one arm.
Valid pattern syntax in macro definitions is different than the pattern syntax
covered in Chapter 18 because macro patterns are matched against Rust code
structure rather than values. Lets walk through what the pieces of the pattern
in Listing D-1 mean; for the full macro pattern syntax, see the reference at
*https://doc.rust-lang.org/stable/reference/macros.html*.
First, a set of parentheses encompasses the whole pattern. Next comes a dollar
sign (`$`) followed by a set of parentheses, which captures values that match
the pattern within the parentheses for use in the replacement code. Within
`$()` is `$x:expr`, which matches any Rust expression and gives the expression
the name `$x`.
The comma following `$()` indicates that a literal comma separator character
could optionally appear after the code that matches the code captured in `$()`.
The `*` following the comma specifies that the pattern matches zero or more of
whatever precedes the `*`.
When we call this macro with `vec![1, 2, 3];`, the `$x` pattern matches three
times with the three expressions `1`, `2`, and `3`.
Now lets look at the pattern in the body of the code associated with this arm:
the `temp_vec.push()` code within the `$()*` part is generated for each part
that matches `$()` in the pattern, zero or more times depending on how many
times the pattern matches. The `$x` is replaced with each expression matched.
When we call this macro with `vec![1, 2, 3];`, the code generated that replaces
this macro call will be the following:
```
let mut temp_vec = Vec::new();
temp_vec.push(1);
temp_vec.push(2);
temp_vec.push(3);
temp_vec
```
Weve defined a macro that can take any number of arguments of any type and can
generate code to create a vector containing the specified elements.
There are some strange corners with `macro_rules!`. In the future, there
will be a second kind of declarative macro with the `macro` keyword that
will work in a similar fashion but fix some of these edge cases. After that
is done, `macro_rules!` will be effectively deprecated. With this
in mind, as well as the fact that most Rust programmers will *use* macros
more than *write* macros, we wont discuss `macro_rules!` any further. To
learn more about how to write macros, consult the online documentation or
other resources, such as “The Little Book of Rust Macros” at
*https://danielkeep.github.io/tlborm/book/index.html*.
### Procedural Macros for Generating Code from Attributes
<!-- This section is mostly different from what's in Appendix D. /Carol -->
The second form of macros is called *procedural macros* because theyre more
like functions (which are a type of procedure). Procedural macros accept some
Rust code as an input, operate on that code, and produce some Rust code as an
output rather than matching against patterns and replacing the code with other
code as declarative macros do.
There are three kinds of procedural macros, but they all work in a similar
fashion. First, the definitions must reside in their own crate with a special
crate type. This is for complex technical reasons that we hope to eliminate in
the future.
Second, using any of these kinds of macros takes on a form like the code shown
in Listing 19-37, where `some_attribute` is a placeholder for using a specific
macro.
Filename: src/lib.rs
```
use proc_macro;
#[some_attribute]
pub fn some_name(input: TokenStream) -> TokenStream {
}
```
Listing 19-37: An example of using a procedural
macro
Procedural macros consist of a function, which is how they get their name:
“procedure” is a synonym for “function.” Why not call them “functional macros”?
Well, one of the types is “function-like,” and that would get confusing.
Anyway, the function defining a procedural macro takes a `TokenStream` as an
input and produces a `TokenStream` as an output. This is the core of the macro:
the source code that the macro is operating on makes up the input
`TokenStream`, and the code the macro produces is the output `TokenStream`.
Finally, the function has an attribute on it; this attribute says which kind of
procedural macro were creating. We can have multiple kinds of procedural
macros in the same crate.
Given that the kinds of macros are so similar, well start with a custom derive
macro. Then well explain the small differences that make the other forms
different.
### How to Write a Custom `derive` Macro
<!-- This section is largely the same as the "Procedural Macros for Custom
derive" section in Appendix D; I can provide a list of the small changes if
that would be helpful. /Carol -->
Lets create a crate named `hello_macro` that defines a trait named
`HelloMacro` with one associated function named `hello_macro`. Rather than
making our crate users implement the `HelloMacro` trait for each of their
types, well provide a procedural macro so users can annotate their type with
`#[derive(HelloMacro)]` to get a default implementation of the `hello_macro`
function. The default implementation will print `Hello, Macro! My name is
TypeName!` where `TypeName` is the name of the type on which this trait has
been defined. In other words, well write a crate that enables another
programmer to write code like Listing 19-38 using our crate.
Filename: src/main.rs
```
use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;
#[derive(HelloMacro)]
struct Pancakes;
fn main() {
Pancakes::hello_macro();
}
```
Listing 19-38: The code a user of our crate will be able to write when using
our procedural macro
This code will print `Hello, Macro! My name is Pancakes!` when were done. The
first step is to make a new library crate, like this:
```
$ cargo new hello_macro --lib
```
Next, well define the `HelloMacro` trait and its associated function:
Filename: src/lib.rs
```
pub trait HelloMacro {
fn hello_macro();
}
```
We have a trait and its function. At this point, our crate user could implement
the trait to achieve the desired functionality, like so:
```
use hello_macro::HelloMacro;
struct Pancakes;
impl HelloMacro for Pancakes {
fn hello_macro() {
println!("Hello, Macro! My name is Pancakes!");
}
}
fn main() {
Pancakes::hello_macro();
}
```
However, they would need to write the implementation block for each type they
wanted to use with `hello_macro`; we want to spare them from having to do this
work.
Additionally, we cant yet provide a default implementation for the
`hello_macro` function that will print the name of the type the trait is
implemented on: Rust doesnt have reflection capabilities, so it cant look up
the types name at runtime. We need a macro to generate code at compile time.
The next step is to define the procedural macro. At the time of this writing,
procedural macros need to be in their own crate. Eventually, this restriction
might be lifted. The convention for structuring crates and macro crates is as
follows: for a crate named `foo`, a custom derive procedural macro crate is
called `foo_derive`. Lets start a new crate called `hello_macro_derive` inside
our `hello_macro` project:
```
$ cargo new hello_macro_derive --lib
```
Our two crates are tightly related, so we create the procedural macro crate
within the directory of our `hello_macro` crate. If we change the trait
definition in `hello_macro`, well have to change the implementation of the
procedural macro in `hello_macro_derive` as well. The two crates will need to
be published separately, and programmers using these crates will need to add
both as dependencies and bring them both into scope. We could instead have the
`hello_macro` crate use `hello_macro_derive` as a dependency and reexport the
procedural macro code. But the way weve structured the project makes it
possible for programmers to use `hello_macro` even if they dont want the
`derive` functionality.
We need to declare the `hello_macro_derive` crate as a procedural macro crate.
Well also need functionality from the `syn` and `quote` crates, as youll see
in a moment, so we need to add them as dependencies. Add the following to the
*Cargo.toml* file for `hello_macro_derive`:
Filename: hello_macro_derive/Cargo.toml
```
[lib]
proc-macro = true
[dependencies]
syn = "0.14.4"
quote = "0.6.3"
```
To start defining the procedural macro, place the code in Listing 19-39 into
your *src/lib.rs* file for the `hello_macro_derive` crate. Note that this code
wont compile until we add a definition for the `impl_hello_macro` function.
Filename: hello_macro_derive/src/lib.rs
```
extern crate proc_macro;
use crate::proc_macro::TokenStream;
use quote::quote;
use syn;
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
// Construct a representation of Rust code as a syntax tree
// that we can manipulate
let ast = syn::parse(input).unwrap();
// Build the trait implementation
impl_hello_macro(&ast)
}
```
Listing 19-39: Code that most procedural macro crates will need to have for
processing Rust code
Notice the way weve split the functions in Listing 19-39; this will be the
same for almost every procedural macro crate you see or create, because it
makes writing a procedural macro more convenient. What you choose to do in the
place where the `impl_hello_macro` function is called will be different
depending on your procedural macros purpose.
Weve introduced three new crates: `proc_macro`, `syn` (available from
*https://crates.io/crates/syn*), and `quote` (available from
*https://crates.io/crates/quote*). The `proc_macro` crate comes with Rust, so
we didnt need to add that to the dependencies in *Cargo.toml*. The
`proc_macro` crate is the compilers API to be able to read and manipulate Rust
code from our code. The `syn` crate parses Rust code from a string into a data
structure that we can perform operations on. The `quote` crate takes `syn` data
structures and turns them back into Rust code. These crates make it much
simpler to parse any sort of Rust code we might want to handle: writing a full
parser for Rust code is no simple task.
The `hello_macro_derive` function will get called when a user of our library
specifies `#[derive(HelloMacro)]` on a type. The reason is that weve annotated
the `hello_macro_derive` function here with `proc_macro_derive` and specified
the name, `HelloMacro`, which matches our trait name; thats the convention
most procedural macros follow.
This function first converts the `input` from a `TokenStream` to a data
structure that we can then interpret and perform operations on. This is where
`syn` comes into play. The `parse` function in `syn` takes a `TokenStream` and
returns a `DeriveInput` struct representing the parsed Rust code. Listing 19-40
shows the relevant parts of the `DeriveInput` struct we get from parsing the
string `struct Pancakes;`:
```
DeriveInput {
// --snip--
ident: Ident {
ident: "Pancakes",
span: #0 bytes(95..103)
},
data: Struct(
DataStruct {
struct_token: Struct,
fields: Unit,
semi_token: Some(
Semi
)
}
)
}
```
Listing 19-40: The `DeriveInput` instance we get when parsing the code that has
the macros attribute in Listing 19-38
The fields of this struct show that the Rust code weve parsed is a unit struct
with the `ident` (identifier, meaning the name) of `Pancakes`. There are more
fields on this struct for describing all sorts of Rust code; check the `syn`
documentation for `DeriveInput` at
*https://docs.rs/syn/0.14.4/syn/struct.DeriveInput.html* for more information.
At this point, we havent defined the `impl_hello_macro` function, which is
where well build the new Rust code we want to include. But before we do, note
that its output is also a `TokenStream`. The returned `TokenStream` is added to
the code that our crate users write, so when they compile their crate, theyll
get extra functionality that we provide.
You might have noticed that were calling `unwrap` to panic if the call to the
`syn::parse` function fails here. Panicking on errors is necessary in
procedural macro code because `proc_macro_derive` functions must return
`TokenStream` rather than `Result` to conform to the procedural macro API.
Weve chosen to simplify this example by using `unwrap`; in production code,
you should provide more specific error messages about what went wrong by using
`panic!` or `expect`.
Now that we have the code to turn the annotated Rust code from a `TokenStream`
into a `DeriveInput` instance, lets generate the code that implements the
`HelloMacro` trait on the annotated type as shown in Listing 19-41.
Filename: hello_macro_derive/src/lib.rs
```
fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let gen = quote! {
impl HelloMacro for #name {
fn hello_macro() {
println!("Hello, Macro! My name is {}", stringify!(#name));
}
}
};
gen.into()
}
```
Listing 19-41: Implementing the `HelloMacro` trait using the parsed Rust code
We get an `Ident` struct instance containing the name (identifier) of the
annotated type using `ast.ident`. The struct in Listing 19-40 shows that the
`ident` we get when the `impl_hello_macro` function is run on the code in
Listing 19-38 will have the `ident` field with a value of `"Pancakes"`. Thus,
the `name` variable in Listing 19-41 will contain an `Ident` struct instance
that, when printed, will be the string `"Pancakes"`, the name of the struct in
Listing 19-38.
The `quote!` macro lets us write the Rust code that we want to return. The
direct result of the `quote!` macros execution isnt whats expected by the
compiler and needs to be converted to a `TokenStream`. We do this by calling
the `into` method, which consumes this intermediate representation and returns
a value of the required `TokenStream` type.
The `quote!` macro also provides some very cool templating mechanics; we can
write `#name`, and `quote!` will replace it with the value in the variable
named `name`. You can even do some repetition similar to the way regular macros
work. Check out the `quote` crates docs at *https://docs.rs/quote* for a
thorough introduction.
We want our procedural macro to generate an implementation of our `HelloMacro`
trait for the type the user annotated, which we can get by using `#name`. The
trait implementation has one function, `hello_macro`, whose body contains the
functionality we want to provide: printing `Hello, Macro! My name is` and then
the name of the annotated type.
The `stringify!` macro used here is built into Rust. It takes a Rust
expression, such as `1 + 2`, and at compile time turns the expression into a
string literal, such as `"1 + 2"`. This is different than `format!` or
`println!`, which evaluate the expression and then turn the result into a
`String`. There is a possibility that the `#name` input might be an expression
to print literally, so we use `stringify!`. Using `stringify!` also saves an
allocation by converting `#name` to a string literal at compile time.
At this point, `cargo build` should complete successfully in both `hello_macro`
and `hello_macro_derive`. Lets hook up these crates to the code in Listing
19-38 to see the procedural macro in action! Create a new binary project in
your *projects* directory using `cargo new pancakes`. We need to add
`hello_macro` and `hello_macro_derive` as dependencies in the `pancakes`
crates *Cargo.toml*. If youre publishing your versions of `hello_macro` and
`hello_macro_derive` to *https://crates.io/*, they would be regular
dependencies; if not, you can specify them as `path` dependencies as follows:
```
[dependencies]
hello_macro = { path = "../hello_macro" }
hello_macro_derive = { path = "../hello_macro/hello_macro_derive" }
```
Put the code from Listing 19-38 into *src/main.rs*, and run `cargo run`: it
should print `Hello, Macro! My name is Pancakes!` The implementation of the
`HelloMacro` trait from the procedural macro was included without the
`pancakes` crate needing to implement it; the `#[derive(HelloMacro)]` added the
trait implementation.
Next, lets explore how the other kinds of procedural macros differ from custom
derive macros.
### Attribute-like macros
<!-- This section is new. /Carol -->
Attribute-like macros are similar to custom derive macros, but instead of
generating code for the `derive` attribute, they allow you to create new
attributes. Theyre also more flexible; `derive` only works for structs and
enums; attributes can go on other items as well, like functions. As an example
of using an attribute-like macro, you might have an attribute named `route`
that annotates functions when using a web application framework:
```
#[route(GET, "/")]
fn index() {
```
This `#[route]` attribute would be defined by the framework itself as a
procedural macro. The macro definition functions signature would look like
this:
```
#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
```
Here, we have two parameters of type `TokenStream`; the first is for the
contents of the attribute itself, that is, the `GET, "/"` part. The second is
the body of the item the attribute is attached to, in this case, `fn index()
{}` and the rest of the functions body.
Other than that, attribute-like macros work the same way as custom derive
macros: create a crate with the `proc-macro` crate type and implement a
function that generates the code you want!
### Function-like macros
<!-- This section is new. /Carol -->
Finally, function-like macros define macros that look like function calls. For
example, an `sql!` macro that might be called like so:
```
let sql = sql!(SELECT * FROM posts WHERE id=1);
```
This macro would parse the SQL statement inside of it and check that its
syntactically correct. This macro would be defined like this:
```
#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
```
This is similar to the custom derive macros signature: we get in the tokens
that are inside of the parentheses, and return the code we wanted to generate.
## Summary
<!-- This section is the same as the existing summary on page 448 and is
included here to show how the new content should fit in. /Carol -->
Whew! Now you have some features of Rust in your toolbox that you wont use
often, but youll know theyre available in very particular circumstances.
Weve introduced several complex topics so that when you encounter them in
error message suggestions or in other peoples code, youll be able to
recognize these concepts and syntax. Use this chapter as a reference to guide
you to solutions.
Next, well put everything weve discussed throughout the book into practice
and do one more project!