Merge pull request #1246 from rust-lang/appendix-edits

Appendix Edits in response to nostarch comments
This commit is contained in:
Carol (Nichols || Goulding)
2018-03-25 10:31:36 -04:00
committed by GitHub
4 changed files with 454 additions and 388 deletions

View File

@@ -1,50 +1,60 @@
## Appendix A: Keywords
The following keywords are reserved by the Rust language and may not be used as
identifiers such as names of functions, variables, parameters, struct fields,
modules, crates, constants, macros, static values, attributes, types, traits,
or lifetimes.
The following is a list of keywords that are reserved for current or future use
by the Rust language. As such, these may not be used as identifiers, such as
names of functions, variables, parameters, struct fields, modules, crates,
constants, macros, static values, attributes, types, traits, or lifetimes.
### Keywords Currently in Use
* `as` - primitive casting, disambiguating the specific trait containing an
item, or renaming items in `use` and `extern crate` statements
* `as` - perform primitive casting, disambiguate the specific trait
containing an item, or rename items in `use` and `extern crate` statements
* `break` - exit a loop immediately
* `const` - constant items and constant raw pointers
* `const` - define constant items or constant raw pointers
* `continue` - continue to the next loop iteration
* `crate` - external crate linkage or a macro variable representing the crate
* `crate` - link an external crate or a macro variable representing the crate
in which the macro is defined
* `else` - fallback for `if` and `if let` control flow constructs
* `enum` - defining an enumeration
* `extern` - external crate, function, and variable linkage
* `enum` - define an enumeration
* `extern` - link an external crate, function, or variable
* `false` - Boolean false literal
* `fn` - function definition and function pointer type
* `for` - iterator loop, part of trait impl syntax, and higher-ranked lifetime
syntax
* `if` - conditional branching
* `impl` - inherent and trait implementation block
* `fn` - define a function or the function pointer type
* `for` - loop over items from an iterator, implement a trait, or specify a
higher-ranked lifetime
* `if` - branch based on the result of a conditional expression
* `impl` - implement inherent or trait functionality
* `in` - part of `for` loop syntax
* `let` - variable binding
* `loop` - unconditional, infinite loop
* `match` - pattern matching
* `mod` - module declaration
* `move` - makes a closure take ownership of all its captures
* `mut` - denotes mutability in references, raw pointers, and pattern bindings
* `pub` - denotes public visibility in struct fields, `impl` blocks, and modules
* `ref` - by-reference binding
* `let` - bind a variable
* `loop` - loop unconditionally
* `match` - match a value to patterns
* `mod` - define a module
* `move` - make a closure take ownership of all its captures
* `mut` - denote mutability in references, raw pointers, or pattern bindings
* `pub` - denote public visibility in struct fields, `impl` blocks, or modules
* `ref` - bind by reference
* `return` - return from function
* `Self` - type alias for the type implementing a trait
* `Self` - a type alias for the type implementing a trait
* `self` - method subject or current module
* `static` - global variable or lifetime lasting the entire program execution
* `struct` - structure definition
* `struct` - define a structure
* `super` - parent module of the current module
* `trait` - trait definition
* `trait` - define a trait
* `true` - Boolean true literal
* `type` - type alias and associated type definition
* `unsafe` - denotes unsafe code, functions, traits, and implementations
* `type` - define a type alias or associated type
* `unsafe` - denote unsafe code, functions, traits, or implementations
* `use` - import symbols into scope
* `where` - type constraint clauses
* `while` - conditional loop
* `where` - denote clauses that constrain a type
* `while` - loop conditionally based on the result of an expression
<!-- we should make sure the definitions for each keyword are consistently
phrased, so for example for enum we say "defining an enumeration" but for fn we
passively call it a "function definition" -- perhaps a good medium would be
"define an enumeration" and "define a function"? Can you go through and make
those consistent? I've attempted it for a few, but am wary of changing meaning.
Also, you may decide to go the passive definition route, which is fine by me,
as long as it's consistent-->
<!-- I've tried, I'm not sure how to be active for keywords that are nouns
though. Please let me know if any still seem inconsistent /Carol -->
### Keywords Reserved for Future Use

View File

@@ -1,5 +1,13 @@
## Appendix B: Operators and Symbols
<!-- We try not to stack headings even in the appendix, can you add some intro
text about what this appendix contains? Quick example below -->
<!-- Done! /Carol -->
This appendix is a glossary of Rust's syntax, including operators and other
symbols that appear by themselves or in the context of paths, generics, trait
bounds, macros, attributes, comments, tuples, and brackets.
### Operators
The following lists the operators in Rust, an example of how the operator would
@@ -7,6 +15,9 @@ appear in context, a short explanation, and whether that operator is
overloadable. If an operator is overloadable, the relevant trait to use to
overload that operator is listed.
<!-- PROD: I'm not sure how to handle this, would it be too big for a table? I
think some structure with aligned columns would make it a great reference -->
* `!` (`ident!(…)`, `ident!{…}`, `ident![…]`): denotes macro expansion.
* `!` (`!expr`): bitwise or logical complement. Overloadable (`Not`).
* `!=` (`var != expr`): nonequality comparison. Overloadable (`PartialEq`).
@@ -33,7 +44,6 @@ overload that operator is listed.
* `..` (`..`, `expr..`, `..expr`, `expr..expr`): right-exclusive range literal.
* `..` (`..expr`): struct literal update syntax.
* `..` (`variant(x, ..)`, `struct_type { x, .. }`): “and the rest” pattern binding.
* `...` (`...expr`, `expr...expr`) *in an expression*: inclusive range expression.
* `...` (`expr...expr`) *in a pattern*: inclusive range pattern.
* `/` (`expr / expr`): arithmetic division. Overloadable (`Div`).
* `/=` (`var /= expr`): arithmetic division and assignment. Overloadable (`DivAssign`).
@@ -66,6 +76,14 @@ overload that operator is listed.
### Non-operator Symbols
<!-- And maybe a quick explanation of what you mean by non-operator
symbols/what counts as a non-operator symbol? -->
<!-- I've tried but it's hard to explain, it's the kind of thing you know when
you see it? /Carol -->
The following lists all non-letters that don't function as operators; that is,
they don't behave like a function or method call.
#### Standalone Syntax
* `'ident`: named lifetime or loop label

View File

@@ -1,63 +1,31 @@
# C - Derivable Traits
## C - Derivable Traits
In various places in the book, we discussed the `derive` attribute that is
applied to a struct or enum. This attribute generates code that implements a
trait on the annotated type with a default implementation. In this example, the
`#[derive(Debug)]` attribute implements the `Debug` trait for the `Point`
struct:
In various places in the book, we've discussed the `derive` attribute
that you can apply to a struct or enum definition.
```rust
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
```
<!-- Above -- I wasn't clear throughout whether the derive attribute is
something passively applied to structs and enums by Rust, or something the
reader applies. I've experimented with making the tone more active, but may
have misinterpreted -- can you make it clear here? Should this be "we've
discussed the `derive` attribute you can apply to a struct or enum"? -->
<!-- Rust never edits your source code file for you. I'm curious to know what
parts of the book have given you that impression... I've tried to clarify here
but now I'm worried about other places in the book... /Carol -->
The code that the compiler generates for the implementation of `Debug` is
similar to this code:
<!-- Below -- Can you lay out what it is we're showing them about derivable
traits in this appendix, just showing them some common ones and how to use
them? -->
<!-- No, we're showing *all* of the derivable traits provided by the standard
library. I guess explaining what we mean by "derivable" was too much of a
tangent for the beginning of this section? I'm not sure where that would fit
instead, so I took it out. So now the text that we had under the "standard
library traits that can be derived" section is here where it seems like you
were expecting it to be /Carol -->
```rust
# struct Point {
# x: i32,
# y: i32,
# }
#
impl ::std::fmt::Debug for Point {
fn fmt(&self, __arg_0: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self {
Point { x: ref __self_0_0, y: ref __self_0_1 } => {
let mut builder = __arg_0.debug_struct("Point");
let _ = builder.field("x", &&(*__self_0_0));
let _ = builder.field("y", &&(*__self_0_1));
builder.finish()
}
}
}
}
```
The generated code implements sensible default behavior for the `Debug` traits
`fmt` function: a `match` expression destructures a `Point` instance into its
field values. Then it builds up a string containing the structs name and each
fields name and value. This means were able to use debug formatting on a
`Point` instance to see what value each field has.
The generated code isnt particularly easy to read because its only for the
compiler to consume, rather than for programmers to read! The `derive`
attribute and the default implementation of `Debug` has saved us all of the
work of writing this code for every struct or enum that we want to be able to
print using debug formatting.
The `derive` attribute has default implementations for the following traits
provided by the standard library. If you want different behavior than what the
`derive` attribute provides, consult the standard library documentation for
each trait for the details needed for manual implementation of the traits.
## Standard Library Traits that Can Be Derived
The following sections list all of the traits in the standard library that can
be used with `derive`. Each section covers:
The `derive` attribute generates code that will implement a trait with its own
default implementation, on the type you have annotated with the `derive`
syntax. In this appendix, we provide a reference of all of the traits in the
standard library that can be used with `derive`. Each section covers:
- What operators and methods deriving this trait will enable
- What the implementation of the trait provided by `derive` does
@@ -65,148 +33,198 @@ be used with `derive`. Each section covers:
- The conditions in which youre allowed or not allowed to implement the trait
- Examples of operations that require the trait
If you want different behavior than that provided by the `derive` attribute,
consult the standard library documentation for each trait for details of how to
manually implement them.
<!-- Liz: I've incorporated the small sections that were after the list of
traits here and then moved the section headings out a level, what do you think?
/Carol -->
The rest of the traits defined in the standard library cant be implemented on
your types using `derive`. These traits dont have sensible default behavior,
so it's up to you to implement them in the way that makes sense for what you're
trying to accomplish.
An example of a trait that cant be derived is `Display`, which handles
formatting for end users. You should always put thought into the appropriate
way to display a type to an end user: what parts of the type should an end user
be allowed to see? What parts would they find relevant? What format of the data
would be most relevant to them? The Rust compiler doesnt have this insight and
so can't provide appropriate default behavior for you.
The list of derivable traits provided in this appendix is not comprehensive:
libraries can implement `derive` for their own traits! In this way, the list of
traits you can use `derive` with is truly open-ended. Implementing `derive`
involves using a procedural macro, which is covered in Appendix D, “Macros.”
### `Debug` for Programmer Output
The `Debug` trait enables debug formatting in format strings, indicated by
adding `:?` within `{}` placeholders.
The `Debug` trait signifies that instances of a type may be printed by
programmers in order to debug their programs by inspecting an instance of a
type at a particular point in a programs execution.
The `Debug` trait allows you to print instances of a type for debugging
purposes, so you and other programmers using your type can inspect an instance
at a particular point in a programs execution.
An example of when `Debug` is required is the `assert_eq!` macro, which prints
the values of the instances given as arguments if the equality assertion fails
so that programmers can see why the two instances werent equal.
`Debug` is required, for example, in use of the `assert_eq!` macro, which
prints the values of instances given as arguments if the equality assertion
fails so programmers can see why the two instances werent equal.
### `PartialEq` and `Eq` for Equality Comparisons
The `PartialEq` trait signifies that instances of a type can be compared to
each other for equality, and enables use of the `==` and `!=` operators.
<!-- I've tried to phrase these definitions in a more active way, it seems like
we're saying using these traits gives us this capabilities --- apologies if
I've misunderstood, feel free to change the phrasing back to the "signifies
that..." version -->
<!-- More active is fine. I feel like it lost a tiny bit of meaning-- not only
can we use these capabilities on our own types, but other programmers using our
types can use these capabilities too. I've tried to reinsert that sentiment
occasionally. /Carol -->
Deriving `PartialEq` implements the `eq` method. When derived on structs, two
instances are equal if all fields are equal, and not equal if any fields are
not equal. When derived on enums, each variant is equal to itself and not equal
to the other variants.
The `PartialEq` trait allows you to compare instances of a type to check for
equality, and enables use of the `==` and `!=` operators.
An example of when `PartialEq` is required is the `assert_eq!` macro, which
needs to be able to compare two instances of a type for equality.
Deriving `PartialEq` implements the `eq` method. When `PartialEq` is derived on
structs, two instances are equal only if *all* fields are equal, and not equal
if any fields are not equal. When derived on enums, each variant is equal to
itself and not equal to the other variants.
The `Eq` trait doesnt have any methods. It only signals that for every value
of the annotated type, the value is equal to itself. The `Eq` trait can only be
applied to types that also implement `PartialEq`. An example of types that
implements `PartialEq` but that cannot implement `Eq` are floating point number
types: the implementation of floating point numbers says that two instances of
the not-a-number value, `NaN`, are not equal to each other.
`PartialEq` is required, for example, with the use of the `assert_eq!` macro,
which needs to be able to compare two instances of a type for equality.
The `Eq` trait has no methods. Its purpose is to signal that for every value of
the annotated type, the value is equal to itself. The `Eq` trait can only be
applied to types that also implement `PartialEq`, though not all types that
implement `PartialEq` can implement `Eq`. One example of this is floating point
number types: the implementation of floating point numbers says that two
instances of the not-a-number value, `NaN`, are not equal to each other.
An example of when `Eq` is required is for keys in a `HashMap` so that the
`HashMap` can tell whether two keys are the same.
### `PartialOrd` and `Ord` for Ordering Comparisons
The `PartialOrd` trait signifies that instances of a type can be compared to
each other to see which is larger than the other for sorting purposes. A type
that implements `PartialOrd` may be used with the `<`, `>`, `<=`, and `>=`
operators. The `PartialOrd` trait can only be applied to types that also
implement `PartialEq`.
The `PartialOrd` trait allows you to compare instances of a type for sorting
purposes. A type that implements `PartialOrd` may be used with the `<`, `>`,
`<=`, and `>=` operators. The `PartialOrd` trait can only be applied to types
that also implement `PartialEq`.
Deriving `PartialOrd` implements the `partial_cmp` method, which returns an
`Option<Ordering>` that may be `None` if comparing the given values does not
produce an ordering. When derived on structs, two instances of the struct are
compared by comparing the value in each field in the order in which the fields
appear in the struct definition. When derived on enums, variants of the enum
declared earlier in the enum definition are greater than the variants listed
later.
`Option<Ordering>` that will be `None` when the values given do not produce an
ordering. An example of a value that doesn't produce an ordering, even though
most values of that type can be compared, is the not-a-number (`NaN`) floating
point value. Calling `partial_cmp` with any floating point number and the `NaN`
floating point value will return `None`.
An example of when `PartialOrd` is required is the `gen_range` method in the
<!-- Above -- you mean when the values cannot be ordered, for example if they
are of types that can't be compared? -->
<!-- No, if they're *types* that can't be compared, then the PartialOrd trait
doesn't apply at all. I've tried to clarify and added an example /Carol-->
When derived on structs, `PartialOrd` compares two instances by comparing the
value in each field in the order in which the fields appear in the struct
definition. When derived on enums, variants of the enum declared earlier in the
enum definition are considered greater than the variants listed later.
`PartialOrd` is required, for example, for the `gen_range` method from the
`rand` crate that generates a random value in the range specified by a low
value and a high value.
The `Ord` trait signifies that for any two value of the annotated type, a valid
ordering exists. The `Ord` trait implements the `cmp` method, which returns an
`Ordering` rather than an `Option<Ordering>` because a valid ordering will
always be possible. The `Ord` trait can only be applied to types that also
implement `PartialOrd` and `Eq` (and `Eq` requires `PartialEq`). When derived
on structs and enums, `cmp` behaves the same way as the derived implementation
for `partial_cmp` does with `PartialOrd`.
The `Ord` trait allows you to know that for any two values of the annotated
type, a valid ordering will exist. The `Ord` trait implements the `cmp` method,
which returns an `Ordering` rather than an `Option<Ordering>` because a valid
ordering will always be possible. The `Ord` trait can only be applied to types
that also implement `PartialOrd` and `Eq` (and `Eq` requires `PartialEq`). When
derived on structs and enums, `cmp` behaves the same way as the derived
implementation for `partial_cmp` does with `PartialOrd`.
An example of when `Ord` is required is when storing values in a `BTreeSet<T>`,
a data structure that stores data based on the sort order of the values.
### `Clone` and `Copy` for Duplicating Values
The `Clone` trait signifies there is a way to explicitly create a duplicate of
a value, and the duplication process might involve running arbitrary code.
Deriving `Clone` implements the `clone` method. When derived, the
implementation of `clone` for the whole type calls `clone` on each of the parts
of the type, so all of the fields or values in the type must also implement
`Clone` to derive `Clone`.
<!-- Below -- I wasn't clear on the arbitrary code section of this explanation.
Are we saying using Clone (as opposed to copy) risks bringing it arbitrary
code? Why use Clone over copy? (I think we might have covered this in an
earlier chapter, so feel free to cross ref there too if that's an easier
explanation) -->
<!-- Yes, we covered this in chapter 4 and I've added a cross reference. /Carol
-->
The `Clone` trait allows you to explicitly create a deep copy of a value, and
the duplication process might involve running arbitrary code and copying heap
data. See the "Ways Variables and Data Interact: Clone" section in Chapter 4
for more information on `Clone`.
Deriving `Clone` implements the `clone` method which, when implemented for the
whole type, calls `clone` on each of the parts of the type. This means all of
the fields or values in the type must also implement `Clone` to derive `Clone`.
An example of when `Clone` is required is when calling the `to_vec` method on a
slice containing instances of some type. The slice doesnt own the instances
but the vector returned from `to_vec` will need to own its instances, so the
implementation of `to_vec` calls `clone` on each item. Thus, the type stored in
the slice must implement `Clone`.
slice. The slice doesnt own the type instances it contains, but the vector
returned from `to_vec` will need to own its instances, so `to_vec` calls
`clone` on each item. Thus, the type stored in the slice must implement `Clone`.
The `Copy` trait signifies that a value can be duplicated by only copying bits;
no other code is necessary. The `Copy` trait does not define any methods to
prevent programmers from overloading those methods violating the assumption
that no arbitrary code is being run. You can derive `Copy` on any type whose
parts all implement `Copy`. The `Copy` trait can only be applied to types that
also implement `Clone`, as a type that implements `Copy` has a trivial
implementation of `Clone`, doing the same thing as `Copy`.
The `Copy` trait allows you to duplicate a value by only copying bits stored on
the stack; no arbitrary code is necessary. See the "Stack-Only Data: Copy"
section in Chapter 4 for more information on `Copy`.
`Copy` is rarely required; when types implement `Copy`, there are optimizations
that can be applied and the code becomes nicer because you dont have to call
`clone`. Everything possible with `Copy` can also be accomplished with `Clone`,
but the code might be slower or have to use `clone` in places.
<!-- I'm not clear on why the clone trait uses arbitrary code but copy doesn't
-- is this important to make clear? -->
<!-- We discussed this in chapter 4; I've added a cross ref. /Carol -->
The `Copy` trait does not define any methods to prevent programmers from
overloading those methods violating the assumption that no arbitrary code is
being run. That way, all programmers can assume that copying a value will be
very fast.
<!-- above -- I couldn't follow this either, what does that mean practically
for the programmer? What does overloading methods that violate the assumption
mean? -->
<!-- I added a sentence at the end of the paragraph, does that clear it up?
/Carol -->
You can derive `Copy` on any type whose parts all implement `Copy`. The `Copy`
trait can only be applied to types that also implement `Clone`, as a type that
implements `Copy` has a trivial implementation of `Clone`, doing the same thing
as `Copy`.
`Copy` is rarely required; types implement `Copy` have optimizations available
mean you don't have to call `clone`, making the code more concise.
<!-- By "nicer" do you mean more efficient and understandable? -->
<!-- concise, I've changed /Carol -->
Everything possible with `Copy` can also be accomplished with `Clone`, but the
code might be slower or have to use `clone` in places.
### `Hash` for Mapping a Value to a Value of Fixed Size
The `Hash` trait signifies there is a way to take an instance of a type that
takes up an arbitrary amount of size and map that instance to a value of fixed
size by using a hash function. Deriving `Hash` implements the `hash` method.
When derived, the implementation of `hash` for the whole type combines the
result of calling `hash` on each of the parts of the type, so all of the fields
or values in the type must also implement `Hash` to derive `Hash`.
The `Hash` trait allows you to take an instance of a type of arbitrary size and
map that instance to a value of fixed size, using a hash function. Deriving
`Hash` implements the `hash` method. The derived implementation of the `hash`
method combines the result of calling `hash` on each of the parts of the type,
meaning all fields or values must also implement `Hash` to derive `Hash`.
An example of when `Hash` is required is for keys in a `HashMap` so that the
`HashMap` can store data efficiently.
An example of when `Hash` is required is in storing keys in a `HashMap`, in
order to store data efficiently.
### `Default` for Default Values
The `Default` trait signifies there is a way to create a default value for a
type. Deriving `Default` implements the `default` method. When derived, the
implementation of `Default` for the whole type calls the `default` method on
each of the parts of the type, so all of the fields or values in the type must
also implement `Default` to derive `Default.`
The `Default` trait allows you to create a default value for a type. Deriving
`Default` implements the `default` method. The derived implementation of the
`default` method calls the `default` method on each part of the type, meaning
all fields or values in the type must also implement `Default` to derive
`Default.`
A common use of `Default::default` is in combination with the struct update
`Default::default` is commonly used in combination with the struct update
syntax discussed in the “Creating Instances From Other Instances With Struct
Update Syntax” section in Chapter 5. You can customize a few fields of a struct
and then use the default values for the rest by using `..Default::default()`.
and then set and use a default value for the rest of the fields by using
`..Default::default()`.
An example of when `Default` is required is the `unwrap_or_default` method on
`Option<T>` instances. If the `Option<T>` is `None`, the `unwrap_or_default`
`Default` is required when, for example, you use the `unwrap_or_default` method
on `Option<T>` instances. If the `Option<T>` is `None`, the `unwrap_or_default`
method will return the result of `Default::default` for the type `T` stored in
the `Option<T>`.
## Standard Library Traits that Cant Be Derived
The rest of the traits defined in the standard library cant be implemented on
your types using `derive`. These traits dont have a sensible default behavior
they could have, so you are required to implement them in the way that makes
sense for what you are trying to accomplish with your code.
An example of a trait that cant be derived is `Display`, which handles
formatting of a type for end users of your programs. You should put thought
into the appropriate way to display a type to an end user: what parts of the
type should an end user be allowed to see? What parts would they find relevant?
What format of the data would be most relevant to them? The Rust compiler
doesnt have this insight into your application, so you must provide it.
## Making Custom Traits Derivable
The above list is not comprehensive, however: libraries can implement `derive`
for their own types! In this way, the list of traits you can use `derive` with
is truly open-ended. Implementing `derive` involves using a procedural macro,
which is covered in the next appendix, “Macros.”

View File

@@ -1,84 +1,85 @@
# D - Macros
## Appendix D: Macros
Weve used macros, such as `println!`, throughout this book. This appendix will
explain:
Weve used macros like `println!` throughout this book, but haven't fully
explored what a macro is and how it works. This appendix will explain:
- What macros are and how they differ from functions
- How to define a declarative macro to do metaprogramming
- How to define a procedural macro to create custom `derive` traits
Macros are covered in an appendix because theyre still evolving. They have
changed and will change more than the rest of the language and standard library
since Rust 1.0, so this section will likely get out of date more than the rest
of this book. The code shown here will still continue to work due to Rusts
We're covering the details of macros in an appendix because theyre still
evolving in Rust. Macros have changed and, in the near future, will change at a
quicker rate than the rest of the language and standard library since Rust 1.0,
so this section is more likely to date than the rest of the book. The code
shown here will still continue to work with future versions, due to Rusts
stability guarantees, but there may be additional capabilities or easier ways
to write macros that arent available at the time of this publication.
to write macros that weren't available at the time of this publication. Bear
that in mind if you try to implement anything from this appendix.
## Macros are More Flexible and Complex than Functions
### The Difference Between Macros and Functions
Fundamentally, macros are a way of writing code that writes other code, which
is known as *metaprogramming*. In the previous appendix, we discussed the
`derive` attribute, which generates an implementation of various traits for
you. Weve also used the `println!` and `vec!` macros. All of these macros
*expand* to produce more code than what youve written in your source code.
Fundamentally, macros are a way of writing code that writes other code, known
as *metaprogramming*. In Appendix C, we discussed 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 yourself.
Metaprogramming is useful to reduce the amount of code you have to write and
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, as we discussed in Chapter 1.
A function signature has to declare the number and type of parameters the
function has. Macros 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,
whereas a function cant because a function gets called at runtime and a trait
needs to be implemented at compile time.
some additional powers that functions dont have.
The downside to implementing a macro rather than a function is that macro
definitions are more complex than function definitions. Youre writing Rust
code that writes Rust code, and macro definitions are generally more difficult
to read, understand, and maintain than function definitions.
A function signature has to 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 over 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.
Another difference between macros and functions is that macro definitions
arent namespaced within modules like function definitions are. In order to
prevent unexpected name clashes when using a crate, when bringing an external
crate into the scope of your project, you have to explicitly bring the macros
into the scope of your project as well with the `#[macro_use]` annotation. This
example would bring all the macros defined in the `serde` crate into the scope
of the current crate:
prevent unexpected name clashes when using external crates, you have to
explicitly bring the macros into the scope of your project at the same time as
bringing the external crate into scope, using the `#[macro_use]` annotation.
The following example would bring all the macros defined in the `serde` crate
into the scope of the current crate:
```rust,ignore
#[macro_use]
extern crate serde;
```
If `extern crate` also brought macros into scope by default, you wouldnt be
allowed to use two crates that happened to define macros with the same name. In
practice this conflict doesnt come up much, but the more crates you use, the
more likely it is.
If `extern crate` was able to bring macros into scope by default without this
explicit annotation, you would be prevented from using two crates that happened
to define macros with the same name. In practice this conflict doesnt come up
much, but the more crates you use, the more likely it is.
One last important difference between macros and functions: macros must be
defined or brought into scope before theyre called in a file. Unlike
functions, where we can define a function at the bottom of a file yet call it
at the top, we always have to define macros before were able to call them.
defined or brought into scope *before* theyre called in a file, whereas
functions can be defined anywhere and called anywhere.
## Declarative Macros with `macro_rules!` for General Metaprogramming
### Declarative Macros with `macro_rules!` for General Metaprogramming
The first form of macros in Rust, and the one thats most widely used, is
called *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 choose the code specified with the matching
pattern when the program runs. Macros also have a value that is compared to
patterns that have code associated with them, but the value is the literal Rust
code passed to the macro, the patterns match the structure of that source code,
and the code associated with each pattern is the code that is generated to
replace the code passed to the macro. This all happens during compilation.
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, but in this case 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 taking a look at how the `vec!` macro is defined. Chapter
8 covered how we can use the `vec!` macro to create a new vector that holds
8 covered how we can use the `vec!` macro to create a new vector with
particular values. For example, this macro creates a new vector with three
integers inside:
@@ -86,12 +87,12 @@ integers inside:
let v: Vec<u32> = vec![1, 2, 3];
```
We can also use `vec!` to make a vector of two integers or a vector of five
string slices. Because we dont know the number or type of values, we cant
define a function that is able to create a new vector with the given elements
like `vec!` can.
We could also use the `vec!` macro to make a vector of two integers or a vector
of five string slices---we wouldn't be able to use a function to do the same
because we wouldnt know the number or type of values up front.
Lets take a look at a slightly simplified definition of the `vec!` macro:
Lets take a look at a slightly simplified definition of the `vec!` macro in
Listing AD-1:
```rust
#[macro_export]
@@ -108,45 +109,61 @@ macro_rules! vec {
}
```
> Note: the actual definition of the `vec!` macro in the standard library also
> has code to pre-allocate the correct amount of memory up-front. That code
> is an optimization that weve chosen not to include here for simplicity.
Listing AD-1: A simplified version of the `vec!` macro definition
> Note: the actual definition of the `vec!` macro in the standard library
> includes code to pre-allocate the correct amount of memory up-front. That
> code is an optimization that weve chosen not to include here to make the
> example simpler.
The `#[macro_export]` annotation indicates that this macro should be made
available when other crates import the crate in which were defining this
macro. Without this annotation, even if someone depending on this crate uses
the `#[macro_use]` annotation, this macro would not be brought into scope.
available whenever the crate in which were defining the macro is imported.
Without this annotation, even if someone depending on this crate uses the
`#[macro_use]` annotation, the macro would not be brought into scope.
Macro definitions start with `macro_rules!` and the name of the macro were
defining without the exclamation mark, which in this case is `vec`. This is
followed by curly brackets denoting the body of the macro definition.
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.
Inside the body is a structure similar to the structure of a `match`
expression. This macro definition has one arm with the pattern `( $( $x:expr
),* )`, followed by `=>` and the block of code associated with this pattern. If
this pattern matches, then the block of code will be emitted. Given that this
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, theres only one valid way to match; any
other will be an error. More complex macros will have more than one arm.
The pattern syntax valid in macro definitions is different than the pattern
syntax covered in Chapter 18 because the patterns are for matching against Rust
code structure rather than values. Lets walk through what the pieces of the
pattern used here mean; for the full macro pattern syntax, see [the reference].
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 AD-1 mean; for the full macro pattern syntax, see [the reference].
[the reference]: ../../reference/macros.html
The `$x:expr` part of the pattern matches any Rust expression and gives the
expression the name `$x`. The `*` specifies that the pattern matches zero or
more of whatever precedes the `*`. In this case, `*` is preceded by `$(),` so
this pattern matches zero or more of whatever is inside the parentheses,
delimited by a comma. When we call this macro with `vec![1, 2, 3];`, the
pattern matches the three expressions `1`, `2`, and `3`.
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`.
In the body of the code associated with this arm, 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` in the code associated with the
arm 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 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 let's 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:
<!-- Above What about temp_vec.push, do you want to quickly mention that? Or do
you mean "The `$()*` part and the content of the parentheses is generated for
each part that matches `$()` in the pattern"-->
<!-- The latter, I've tried to clarify /Carol -->
```rust,ignore
let mut temp_vec = Vec::new();
@@ -166,27 +183,27 @@ as [The Little Book of Rust Macros][tlborm].
[tlborm]: https://danielkeep.github.io/tlborm/book/index.html
## Procedural Macros for Custom `derive`
### Procedural Macros for Custom `derive`
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. Today, the only thing you can define procedural
macros for is to allow your traits to be implemented on a type by specifying
the trait name in a `derive` annotation.
code as declarative macros do. At the time of writing, you can only really
define procedural macros to allow your traits to be implemented on a type by
specifying the trait name in a `derive` annotation.
Lets create a crate named `hello_macro` that defines a trait named
We're going to create a crate named `hello_macro` that defines a trait named
`HelloMacro` with one associated function named `hello_macro`. Rather than
making users of our crate implement the `HelloMacro` trait for each of their
types, wed like users to be able to annotate their type with
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 associated with their type. 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.
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, were going to write a crate that enables another programmer to
write code that looks like Listing A4-1 using our crate:
write code like Listing AD-2 using our crate:
<span class="filename">Filename: src/main.rs</span>
@@ -205,19 +222,17 @@ fn main() {
}
```
<span class="caption">Listing A4-1: The code a user of our crate will be able
to write when weve written the procedural macro</span>
<span class="caption">Listing AD-2: The code a user of our crate will be able
to write with use of our procedural macro</span>
This code will print `Hello, Macro! My name is Pancakes!` when were done. Lets
get started!
Lets make a new library crate:
get started! First we need to make a new library crate:
```text
$ cargo new hello_macro
$ cargo new hello_macro --lib
```
First, well define the `HelloMacro` trait and associated function:
Now well define the `HelloMacro` trait and its associated function:
<span class="filename">Filename: src/lib.rs</span>
@@ -227,8 +242,9 @@ pub trait HelloMacro {
}
```
At this point, a user of our crate could implement the trait themselves to
achieve the functionality we wanted to enable, like so:
We have a trait and its function. At this point, a user of our crate would be
able to implement the trait themselves to achieve the desired functionality,
like so:
```rust,ignore
extern crate hello_macro;
@@ -249,43 +265,43 @@ fn main() {
```
However, they would need to write out the implementation block for each type
they wanted to be able to use with `hello_macro`; wed like to make using our
trait more convenient for other programmers by saving them this work.
they wanted to use with `hello_macro`; wed like to save them this work.
Additionally, we cant provide a default implementation for the `hello_macro`
function that has the behavior we want of printing out the name of the type the
trait is implemented on: Rust doesnt have reflection capabilities, so we cant
look up the types name at runtime. We need a macro to generate code at compile
time.
Additionally, we cant yet provide a default implementation for the
`hello_macro` function that will print out the name of the type the trait is
implemented on: Rust doesnt have reflection capabilities, so cant look up the
types name at runtime. We need a macro to generate code at compile time.
### Defining Procedural Macros Requires a Separate Crate
<!--Defining Procedural Macros Requires a Separate Crate--> <!-- Since this is
a lone subheading, okay to merge with the general procedural macros section? -->
<!-- Sure /Carol -->
The next step is to define the procedural macro. At the moment, procedural
macros need to be in their own crate. Eventually, this restriction may be
lifted, but for now, its required. As such, theres a convention: 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:
The next step is to define the procedural macro. At the time of writing,
procedural macros need to be in their own crate. Eventually, this restriction
may be lifted. The convention for structuring crates and macro crates is as
such: 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:
```text
$ cargo new hello_macro_derive
$ cargo new hello_macro_derive --lib
```
Weve chosen to create the procedural macro crate within the directory of our
`hello_macro` crate because the two crates are tightly related: 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. Its possible to
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, though, 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
re-export the procedural macro code, but structuring the project this way makes
it possible for programmers to easily decide they only want to use
`hello_macro` if they dont want the `derive` functionality.
re-export the procedural macro code, but the way we've structured the project
makes it possible for programmers to use `hello_macro` even if they dont want
the `derive` functionality.
We need to declare that the `hello_macro_derive` crate is a procedural macro
crate. We also need to add dependencies on the `syn` and `quote` crates to get
useful functionality for operating on Rust code. To do these two things, add
the following to the *Cargo.toml* for `hello_macro_derive`:
We need to declare the `hello_macro_derive` crate as a procedural macro crate.
We'll also need functionality from the `syn` and `quote` crates, as we'll see
in a moment, so we need to add them as dependencies. Add the following to the
*Cargo.toml* file for `hello_macro_derive`:
<span class="filename">Filename: hello_macro_derive/Cargo.toml</span>
@@ -298,13 +314,14 @@ syn = "0.11.11"
quote = "0.3.15"
```
To start defining the procedural macro, place the code from Listing A4-2 in
*src/lib.rs* for the `hello_macro_derive` crate. Note that this wont compile
until we add a definition for the `impl_hello_macro` function. Weve split the
code into functions in this way because the code in Listing A4-2 will be the
same for almost every procedural macro crate; its code that makes writing a
To start defining the procedural macro, place the code from Listing AD-3 in
your *src/lib.rs* for the `hello_macro_derive` crate. Note that this wont
compile until we add a definition for the `impl_hello_macro` function.
Note the way we've split the functions in AD-3; this will be the same for
almost every procedural macro crate you see or create, as 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 and depend on the
`impl_hello_macro` function is called will be different depending on the
purpose of your procedural macro.
<span class="filename">Filename: hello_macro_derive/src/lib.rs</span>
@@ -333,7 +350,7 @@ pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
}
```
<span class="caption">Listing A4-2: Code that most procedural macro crates will
<span class="caption">Listing AD-3: Code that most procedural macro crates will
need to have for processing Rust code</span>
We have introduced three new crates: `proc_macro`, [`syn`], and [`quote`]. The
@@ -348,27 +365,31 @@ to handle: writing a full parser for Rust code is no simple task.
[`syn`]: https://crates.io/crates/syn
[`quote`]: https://crates.io/crates/quote
The `hello_macro_derive` function is the code that will get called when a user
of our library specifies the `#[derive(HelloMacro)]` annotation on a type
because weve annotated the `hello_macro_derive` function here with
`proc_macro_derive` and specified the same name, `HelloMacro`. This name
matches our trait named `HelloMacro`; thats the convention most procedural
macros follow.
The `hello_macro_derive` function will get called when a user of our library
specifies `#[derive(HelloMacro)]` on a type, because 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.
The first thing this function does is convert the `input` from a `TokenStream`
to a `String` by calling `to_string`. This `String` is a string representation
of the Rust code for which we are deriving `HelloMacro`. In the example in
Listing A4-1, `s` will have the `String` value `struct Pancakes;` because
thats the Rust code we added the `#[derive(HelloMacro)]` annotation to.
This function first converts the `input` from a `TokenStream` to a `String` by
calling `to_string`. This `String` is a string representation of the Rust code
for which we are deriving `HelloMacro`. In the example in Listing AD-2, `s`
will have the `String` value `struct Pancakes;` because thats the Rust code we
added the `#[derive(HelloMacro)]` annotation to.
At the moment, the only thing you can do with a `TokenStream` is convert it to
a string. A richer API will exist in the future.
<!-- I'm not sure why we convert to a string then to a structure we can use,
will that be clear to the reader here? -->
<!-- This is just how procedural macros work and what you have to do, which is
why we have the next note. /Carol -->
What we really need is to be able to parse the Rust code `String` into a data
structure that we can then interpret and perform operations on. This is where
`syn` comes to play. The `parse_derive_input` function in `syn` takes a
`String` and returns a `DeriveInput` struct representing the parsed Rust code.
Heres the relevant parts of the `DeriveInput` struct we get from parsing the
> Note: At the time of writing, the only thing you can do with a `TokenStream`
> is convert it to a string. A richer API will exist in the future.
Now we need to be able to parse the Rust code `String` into a data structure
that we can then interpret and perform operations on. This is where `syn` comes
to play. The `parse_derive_input` function in `syn` takes a `String` and
returns a `DeriveInput` struct representing the parsed Rust code. The following
shows the relevant parts of the `DeriveInput` struct we get from parsing the
string `struct Pancakes;`:
```rust,ignore
@@ -391,27 +412,25 @@ API docs for `DeriveInput`][syn-docs] for more information.
[syn-docs]: https://docs.rs/syn/0.11.11/syn/struct.DeriveInput.html
We havent defined the `impl_hello_macro` function; thats where well build
the new Rust code we want to include. Before we get to that, the last part of
this `hello_macro_derive` function is using the `quote` crates `parse`
function to turn the output of the `impl_hello_macro` function back into a
`TokenStream`. The returned `TokenStream` is added to the code that users of
our crate write so that when they compile their crate, they get extra
functionality we provide.
At this point we havent defined the `impl_hello_macro` function, which is
where well build the new Rust code we want to include. Before we get to that,
the last part of this `hello_macro_derive` function is using the `parse`
function from the `quote` crate to turn the output of the `impl_hello_macro`
function back into a `TokenStream`. The returned `TokenStream` is added to the
code that users of our crate write so that when they compile their crate, they
get extra functionality we provide.
You may have noticed that were calling `unwrap` to panic if the calls to the
`parse_derive_input` or `parse` functions fail because theyre unable to parse
the `TokenStream` or generate a `TokenStream`. Panicking on errors is necessary
in procedural macro code because `proc_macro_derive` functions must return
`TokenStream` rather than `Result` in order to conform to the procedural macro
API. Weve chosen to keep this example simple by using `unwrap`; in production
code you should provide more specific error messages about what went wrong by
using `expect` or `panic!`.
`parse_derive_input` or `parse` functions fail here. Panicking on errors is
necessary in procedural macro code because `proc_macro_derive` functions must
return `TokenStream` rather than `Result` in order to conform to the procedural
macro API. Weve chosen to keep this example simple by using `unwrap`; in
production code you should provide more specific error messages about what went
wrong by using `expect` or `panic!`.
Now that we have the code to turn the annotated Rust code from a `TokenStream`
into a `String` and into a `DeriveInput` instance, lets write the code that
will generate the code implementing the `HelloMacro` trait on the annotated
type:
into a `String` and a `DeriveInput` instance, lets generate the code
implementing the `HelloMacro` trait on the annotated type:
<span class="filename">Filename: hello_macro_derive/src/lib.rs</span>
@@ -428,35 +447,36 @@ fn impl_hello_macro(ast: &syn::DeriveInput) -> quote::Tokens {
}
```
We are able to get an `Ident` struct instance containing the name (identifier)
of the annotated type using `ast.ident`. With the code from Listing A4-1,
`name` will be `Ident("Pancakes")`.
We get an `Ident` struct instance containing the name (identifier) of the
annotated type using `ast.ident`. With the code from Listing AD-2, `name` will
be `Ident("Pancakes")`.
The `quote!` macro from the `quote` crate lets us write up the Rust code that
we wish to return and convert it into `quote::Tokens`. The `quote!` macro lets
us use some really 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][quote-docs] for a thorough introduction.
The `quote!` macro lets us write up the Rust code that we want to return and
convert it into `quote::Tokens`. This macro also provides some really 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][quote-docs] for a thorough introduction.
[quote-docs]: https://docs.rs/quote
What we want to do for our procedural macro is generate an implementation of
our `HelloMacro` trait for the type the user of our crate has annotated, which
we can get by using `#name`. The trait implementation has one function,
`hello_macro`, and the function body contains the functionality we want to
provide: printing `Hello, Macro! My name is` and then the name of the type the
user of our crate has annotated. 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`. Theres a possibility that `#name` would be an expression that
we would want to print out literally, and `stringify!` also saves an allocation
by converting `#name` to a string literal at compile time.
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`. Theres a possibility that the `#name` input might be an expression
to print out 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 these crates up to the code in Listing
A4-1 to see it in action! Create a new binary project in your `projects`
AD-2 to see it in action! Create a new binary project in your `projects`
directory with `cargo new --bin pancakes`. We need to add both `hello_macro`
and `hello_macro_derive` as dependencies in the `pancakes` crates
*Cargo.toml*. If youve chosen to publish your versions of `hello_macro` and
@@ -469,16 +489,16 @@ hello_macro = { path = "../hello_macro" }
hello_macro_derive = { path = "../hello_macro/hello_macro_derive" }
```
Put the code from Listing A4-1 into *src/main.rs*, and executing `cargo run`
should print `Hello, Macro! My name is Pancakes`! The implementation of the
Put the code from Listing AD-2 into *src/main.rs*, and when you 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)]` took care
of adding the trait implementation.
## The Future of Macros
### The Future of Macros
In the future, well be expanding both declarative and procedural macros. A
better declarative macro system will be used with the `macro` keyword, and
well add more types of procedural macros, for more powerful tasks than only
In the future, well be expanding both declarative and procedural macros. Rust
will use better declarative macro system with the `macro` keyword, and well
add more types of procedural macros, for more powerful tasks than just
`derive`. These systems are still under development at the time of publication;
please consult the online Rust documentation for the latest information.