RustBook/ch19-06-macros.html

697 lines
55 KiB
HTML
Raw Permalink Normal View History

2020-01-06 20:57:15 +00:00
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Macros - The Rust Programming Language</title>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
<link rel="shortcut icon" href="favicon.png">
<link rel="stylesheet" href="css/variables.css">
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" href="css/chrome.css">
<link rel="stylesheet" href="css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
<link href="googleFonts/css.css" rel="stylesheet" type="text/css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="highlight.css">
<link rel="stylesheet" href="tomorrow-night.css">
<link rel="stylesheet" href="ayu-highlight.css">
<!-- Custom theme stylesheets -->
<link rel="stylesheet" href="ferris.css">
<link rel="stylesheet" href="theme/2018-edition.css">
</head>
<body>
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded affix "><a href="title-page.html">The Rust Programming Language</a></li><li class="expanded affix "><a href="foreword.html">Foreword</a></li><li class="expanded affix "><a href="ch00-00-introduction.html">Introduction</a></li><li class="expanded "><a href="ch01-00-getting-started.html"><strong aria-hidden="true">1.</strong> Getting Started</a></li><li><ol class="section"><li class="expanded "><a href="ch01-01-installation.html"><strong aria-hidden="true">1.1.</strong> Installation</a></li><li class="expanded "><a href="ch01-02-hello-world.html"><strong aria-hidden="true">1.2.</strong> Hello, World!</a></li><li class="expanded "><a href="ch01-03-hello-cargo.html"><strong aria-hidden="true">1.3.</strong> Hello, Cargo!</a></li></ol></li><li class="expanded "><a href="ch02-00-guessing-game-tutorial.html"><strong aria-hidden="true">2.</strong> Programming a Guessing Game</a></li><li class="expanded "><a href="ch03-00-common-programming-concepts.html"><strong aria-hidden="true">3.</strong> Common Programming Concepts</a></li><li><ol class="section"><li class="expanded "><a href="ch03-01-variables-and-mutability.html"><strong aria-hidden="true">3.1.</strong> Variables and Mutability</a></li><li class="expanded "><a href="ch03-02-data-types.html"><strong aria-hidden="true">3.2.</strong> Data Types</a></li><li class="expanded "><a href="ch03-03-how-functions-work.html"><strong aria-hidden="true">3.3.</strong> Functions</a></li><li class="expanded "><a href="ch03-04-comments.html"><strong aria-hidden="true">3.4.</strong> Comments</a></li><li class="expanded "><a href="ch03-05-control-flow.html"><strong aria-hidden="true">3.5.</strong> Control Flow</a></li></ol></li><li class="expanded "><a href="ch04-00-understanding-ownership.html"><strong aria-hidden="true">4.</strong> Understanding Ownership</a></li><li><ol class="section"><li class="expanded "><a href="ch04-01-what-is-ownership.html"><strong aria-hidden="true">4.1.</strong> What is Ownership?</a></li><li class="expanded "><a href="ch04-02-references-and-borrowing.html"><strong aria-hidden="true">4.2.</strong> References and Borrowing</a></li><li class="expanded "><a href="ch04-03-slices.html"><strong aria-hidden="true">4.3.</strong> The Slice Type</a></li></ol></li><li class="expanded "><a href="ch05-00-structs.html"><strong aria-hidden="true">5.</strong> Using Structs to Structure Related Data</a></li><li><ol class="section"><li class="expanded "><a href="ch05-01-defining-structs.html"><strong aria-hidden="true">5.1.</strong> Defining and Instantiating Structs</a></li><li class="expanded "><a href="ch05-02-example-structs.html"><strong aria-hidden="true">5.2.</strong> An Example Program Using Structs</a></li><li class="expanded "><a href="ch05-03-method-syntax.html"><strong aria-hidden="true">5.3.</strong> Method Syntax</a></li></ol></li><li class="expanded "><a href="ch06-00-enums.html"><strong aria-hidden="true">6.</strong> Enums and Pattern Matching</a></li><li><ol class="section"><li class="expanded "><a href="ch06-01-defining-an-enum.html"><strong aria-hidden="true">6.1.</strong> Defining an Enum</a></li><li class="expanded "><a href="ch06-02-match.html"><strong aria-hidden="true">6.2.</strong> The match Control Flow Operator</a></li><li class="expanded "><a href="ch06-03-if-let.html"><strong aria-hidden="true">6.3.</strong> Concise Control Flow with if let</a></li></ol></li><li class="expanded "><a href="ch07-00-managing-growing-projects-with-packages-crates-and-modules.html"><strong aria-hidden="true">7.</strong> Managing Growing Projects with Packages, Crates, and Modules</a></li><li><ol class="section"><li class="expanded "><a href="ch07-01-packages-and-crates.html"><strong aria-hidden="true">7.1.</strong> Packages and Crates</a></li><li class="expanded "><a href="ch07-02-defining-modules-to-control-scope-and-privacy.html"><strong aria-hidden="true">7.2.</strong> Defining Modules to Control Scope and Privacy</a></li><li class="expanded "><a href="ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html"><
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar" class="menu-bar">
<div id="menu-bar-sticky-container">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">The Rust Programming Language</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
</div>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h2><a class="header" href="#macros" id="macros">Macros</a></h2>
<p>Weve used macros like <code>println!</code> throughout this book, but we havent fully
explored what a macro is and how it works. The term <em>macro</em> refers to a family
of features in Rust: <em>declarative</em> macros with <code>macro_rules!</code> and three kinds
of <em>procedural</em> macros:</p>
<ul>
<li>Custom <code>#[derive]</code> macros that specify code added with the <code>derive</code> attribute
used on structs and enums</li>
<li>Attribute-like macros that define custom attributes usable on any item</li>
<li>Function-like macros that look like function calls but operate on the tokens
specified as their argument</li>
</ul>
<p>Well talk about each of these in turn, but first, lets look at why we even
need macros when we already have functions.</p>
<h3><a class="header" href="#the-difference-between-macros-and-functions" id="the-difference-between-macros-and-functions">The Difference Between Macros and Functions</a></h3>
<p>Fundamentally, macros are a way of writing code that writes other code, which
is known as <em>metaprogramming</em>. In Appendix C, we discuss the <code>derive</code>
attribute, which generates an implementation of various traits for you. Weve
also used the <code>println!</code> and <code>vec!</code> macros throughout the book. All of these
macros <em>expand</em> to produce more code than the code youve written manually.</p>
<p>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.</p>
<p>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 <code>println!(&quot;hello&quot;)</code> with one argument or
<code>println!(&quot;hello {}&quot;, name)</code> 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.</p>
<p>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.</p>
<p>Another important difference between macros and functions is that you must
define macros or bring them into scope <em>before</em> you call them in a file, as
opposed to functions you can define anywhere and call anywhere.</p>
<h3><a class="header" href="#declarative-macros-with-macro_rules-for-general-metaprogramming" id="declarative-macros-with-macro_rules-for-general-metaprogramming">Declarative Macros with <code>macro_rules!</code> for General Metaprogramming</a></h3>
<p>The most widely used form of macros in Rust is <em>declarative macros</em>. These are
also sometimes referred to as “macros by example,” “<code>macro_rules!</code> macros,” or
just plain “macros.” At their core, declarative macros allow you to write
something similar to a Rust <code>match</code> expression. As discussed in Chapter 6,
<code>match</code> 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 are
associated with particular code: 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, when
matched, replaces the code passed to the macro. This all happens during
compilation.</p>
<p>To define a macro, you use the <code>macro_rules!</code> construct. Lets explore how to
use <code>macro_rules!</code> by looking at how the <code>vec!</code> macro is defined. Chapter 8
covered how we can use the <code>vec!</code> macro to create a new vector with particular
values. For example, the following macro creates a new vector containing three
integers:</p>
<pre><pre class="playpen"><code class="language-rust">
<span class="boring">#![allow(unused_variables)]
</span><span class="boring">fn main() {
</span>let v: Vec&lt;u32&gt; = vec![1, 2, 3];
<span class="boring">}
</span></code></pre></pre>
<p>We could also use the <code>vec!</code> 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.</p>
<p>Listing 19-28 shows a slightly simplified definition of the <code>vec!</code> macro.</p>
<p><span class="filename">Filename: src/lib.rs</span></p>
<pre><pre class="playpen"><code class="language-rust">
<span class="boring">#![allow(unused_variables)]
</span><span class="boring">fn main() {
</span>#[macro_export]
macro_rules! vec {
( $( $x:expr ),* ) =&gt; {
{
let mut temp_vec = Vec::new();
$(
temp_vec.push($x);
)*
temp_vec
}
};
}
<span class="boring">}
</span></code></pre></pre>
<p><span class="caption">Listing 19-28: A simplified version of the <code>vec!</code> macro
definition</span></p>
<blockquote>
<p>Note: The actual definition of the <code>vec!</code> 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.</p>
</blockquote>
<p>The <code>#[macro_export]</code> annotation indicates that this macro should be made
available whenever the crate in which the macro is defined is brought into
scope. Without this annotation, the macro cant be brought into scope.</p>
<p>We then start the macro definition with <code>macro_rules!</code> and the name of the
macro were defining <em>without</em> the exclamation mark. The name, in this case
<code>vec</code>, is followed by curly brackets denoting the body of the macro definition.</p>
<p>The structure in the <code>vec!</code> body is similar to the structure of a <code>match</code>
expression. Here we have one arm with the pattern <code>( $( $x:expr ),* )</code>,
followed by <code>=&gt;</code> 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 pattern will result in an error. More complex macros will have more than
one arm.</p>
<p>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 pattern pieces in
Listing 19-28 mean; for the full macro pattern syntax, see <a href="../reference/macros.html">the reference</a>.</p>
<p>First, a set of parentheses encompasses the whole pattern. A dollar sign (<code>$</code>)
is next, followed by a set of parentheses that captures values that match the
pattern within the parentheses for use in the replacement code. Within <code>$()</code> is
<code>$x:expr</code>, which matches any Rust expression and gives the expression the name
<code>$x</code>.</p>
<p>The comma following <code>$()</code> indicates that a literal comma separator character
could optionally appear after the code that matches the code in <code>$()</code>. The <code>*</code>
specifies that the pattern matches zero or more of whatever precedes the <code>*</code>.</p>
<p>When we call this macro with <code>vec![1, 2, 3];</code>, the <code>$x</code> pattern matches three
times with the three expressions <code>1</code>, <code>2</code>, and <code>3</code>.</p>
<p>Now lets look at the pattern in the body of the code associated with this arm:
<code>temp_vec.push()</code> within <code>$()*</code> is generated for each part that matches <code>$()</code>
in the pattern zero or more times depending on how many times the pattern
matches. The <code>$x</code> is replaced with each expression matched. When we call this
macro with <code>vec![1, 2, 3];</code>, the code generated that replaces this macro call
will be the following:</p>
<pre><code class="language-rust ignore">let mut temp_vec = Vec::new();
temp_vec.push(1);
temp_vec.push(2);
temp_vec.push(3);
temp_vec
</code></pre>
<p>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.</p>
<p>There are some strange edge cases with <code>macro_rules!</code>. In the future, Rust will
have a second kind of declarative macro that will work in a similar fashion but
fix some of these edge cases. After that update, <code>macro_rules!</code> will be
effectively deprecated. With this in mind, as well as the fact that most Rust
programmers will <em>use</em> macros more than <em>write</em> macros, we wont discuss
<code>macro_rules!</code> any further. To learn more about how to write macros, consult
the online documentation or other resources, such as <a href="https://danielkeep.github.io/tlborm/book/index.html">“The Little Book of Rust
Macros”</a>.</p>
<h3><a class="header" href="#procedural-macros-for-generating-code-from-attributes" id="procedural-macros-for-generating-code-from-attributes">Procedural Macros for Generating Code from Attributes</a></h3>
<p>The second form of macros is <em>procedural macros</em>, which act more like functions
(and are a type of procedure). Procedural macros accept some code as an input,
operate on that code, and produce some code as an output rather than matching
against patterns and replacing the code with other code as declarative macros
do.</p>
<p>The three kinds of procedural macros (custom derive, attribute-like, and
function-like) all work in a similar fashion.</p>
<p>When creating procedural macros, 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. Using procedural macros looks like the code in
Listing 19-29, where <code>some_attribute</code> is a placeholder for using a specific
macro.</p>
<p><span class="filename">Filename: src/lib.rs</span></p>
<pre><code class="language-rust ignore">use proc_macro;
#[some_attribute]
pub fn some_name(input: TokenStream) -&gt; TokenStream {
}
</code></pre>
<p><span class="caption">Listing 19-29: An example of using a procedural
macro</span></p>
<p>The function that defines a procedural macro takes a <code>TokenStream</code> as an input
and produces a <code>TokenStream</code> as an output. The <code>TokenStream</code> type is defined by
the <code>proc_macro</code> crate that is included with Rust and represents a sequence of
tokens. This is the core of the macro: the source code that the macro is
operating on makes up the input <code>TokenStream</code>, and the code the macro produces
is the output <code>TokenStream</code>. The function also has an attribute attached to it
that specifies which kind of procedural macro were creating. We can have
multiple kinds of procedural macros in the same crate.</p>
<p>Lets look at the different kinds of procedural macros. Well start with a
custom derive macro and then explain the small dissimilarities that make the
other forms different.</p>
<h3><a class="header" href="#how-to-write-a-custom-derive-macro" id="how-to-write-a-custom-derive-macro">How to Write a Custom <code>derive</code> Macro</a></h3>
<p>Lets create a crate named <code>hello_macro</code> that defines a trait named
<code>HelloMacro</code> with one associated function named <code>hello_macro</code>. Rather than
making our crate users implement the <code>HelloMacro</code> trait for each of their
types, well provide a procedural macro so users can annotate their type with
<code>#[derive(HelloMacro)]</code> to get a default implementation of the <code>hello_macro</code>
function. The default implementation will print <code>Hello, Macro! My name is TypeName!</code> where <code>TypeName</code> 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-30 using our crate.</p>
<p><span class="filename">Filename: src/main.rs</span></p>
<pre><code class="language-rust ignore">use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;
#[derive(HelloMacro)]
struct Pancakes;
fn main() {
Pancakes::hello_macro();
}
</code></pre>
<p><span class="caption">Listing 19-30: The code a user of our crate will be able
to write when using our procedural macro</span></p>
<p>This code will print <code>Hello, Macro! My name is Pancakes!</code> when were done. The
first step is to make a new library crate, like this:</p>
<pre><code class="language-text">$ cargo new hello_macro --lib
</code></pre>
<p>Next, well define the <code>HelloMacro</code> trait and its associated function:</p>
<p><span class="filename">Filename: src/lib.rs</span></p>
<pre><pre class="playpen"><code class="language-rust">
<span class="boring">#![allow(unused_variables)]
</span><span class="boring">fn main() {
</span>pub trait HelloMacro {
fn hello_macro();
}
<span class="boring">}
</span></code></pre></pre>
<p>We have a trait and its function. At this point, our crate user could implement
the trait to achieve the desired functionality, like so:</p>
<pre><code class="language-rust ignore">use hello_macro::HelloMacro;
struct Pancakes;
impl HelloMacro for Pancakes {
fn hello_macro() {
println!(&quot;Hello, Macro! My name is Pancakes!&quot;);
}
}
fn main() {
Pancakes::hello_macro();
}
</code></pre>
<p>However, they would need to write the implementation block for each type they
wanted to use with <code>hello_macro</code>; we want to spare them from having to do this
work.</p>
<p>Additionally, we cant yet provide the <code>hello_macro</code> function with default
implementation 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.</p>
<p>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 <code>foo</code>, a custom derive procedural macro crate is
called <code>foo_derive</code>. Lets start a new crate called <code>hello_macro_derive</code> inside
our <code>hello_macro</code> project:</p>
<pre><code class="language-text">$ cargo new hello_macro_derive --lib
</code></pre>
<p>Our two crates are tightly related, so we create the procedural macro crate
within the directory of our <code>hello_macro</code> crate. If we change the trait
definition in <code>hello_macro</code>, well have to change the implementation of the
procedural macro in <code>hello_macro_derive</code> 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
<code>hello_macro</code> crate use <code>hello_macro_derive</code> as a dependency and re-export the
procedural macro code. However, the way weve structured the project makes it
possible for programmers to use <code>hello_macro</code> even if they dont want the
<code>derive</code> functionality.</p>
<p>We need to declare the <code>hello_macro_derive</code> crate as a procedural macro crate.
Well also need functionality from the <code>syn</code> and <code>quote</code> crates, as youll see
in a moment, so we need to add them as dependencies. Add the following to the
<em>Cargo.toml</em> file for <code>hello_macro_derive</code>:</p>
<p><span class="filename">Filename: hello_macro_derive/Cargo.toml</span></p>
<pre><code class="language-toml">[lib]
proc-macro = true
[dependencies]
syn = &quot;0.14.4&quot;
quote = &quot;0.6.3&quot;
</code></pre>
<p>To start defining the procedural macro, place the code in Listing 19-31 into
your <em>src/lib.rs</em> file for the <code>hello_macro_derive</code> crate. Note that this code
wont compile until we add a definition for the <code>impl_hello_macro</code> function.</p>
<p><span class="filename">Filename: hello_macro_derive/src/lib.rs</span></p>
<!--
This usage of `extern crate` is required for the moment with 1.31.0, see:
https://github.com/rust-lang/rust/issues/54418
https://github.com/rust-lang/rust/pull/54658
https://github.com/rust-lang/rust/issues/55599
-->
<pre><code class="language-rust ignore">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) -&gt; 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(&amp;ast)
}
</code></pre>
<p><span class="caption">Listing 19-31: Code that most procedural macro crates
will require in order to process Rust code</span></p>
<p>Notice that weve split the code into the <code>hello_macro_derive</code> function, which
is responsible for parsing the <code>TokenStream</code>, and the <code>impl_hello_macro</code>
function, which is responsible for transforming the syntax tree: this makes
writing a procedural macro more convenient. The code in the outer function
(<code>hello_macro_derive</code> in this case) will be the same for almost every
procedural macro crate you see or create. The code you specify in the body of
the inner function (<code>impl_hello_macro</code> in this case) will be different
depending on your procedural macros purpose.</p>
<p>Weve introduced three new crates: <code>proc_macro</code>, <a href="https://crates.io/crates/syn"><code>syn</code></a>, and <a href="https://crates.io/crates/quote"><code>quote</code></a>. The
<code>proc_macro</code> crate comes with Rust, so we didnt need to add that to the
dependencies in <em>Cargo.toml</em>. The <code>proc_macro</code> crate is the compilers API that
allows us to read and manipulate Rust code from our code.</p>
<p>The <code>syn</code> crate parses Rust code from a string into a data structure that we
can perform operations on. The <code>quote</code> crate turns <code>syn</code> data structures 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.</p>
<p>The <code>hello_macro_derive</code> function will be called when a user of our library
specifies <code>#[derive(HelloMacro)]</code> on a type. This is possible because weve
annotated the <code>hello_macro_derive</code> function here with <code>proc_macro_derive</code> and
specified the name, <code>HelloMacro</code>, which matches our trait name; this is the
convention most procedural macros follow.</p>
<p>The <code>hello_macro_derive</code> function first converts the <code>input</code> from a
<code>TokenStream</code> to a data structure that we can then interpret and perform
operations on. This is where <code>syn</code> comes into play. The <code>parse</code> function in
<code>syn</code> takes a <code>TokenStream</code> and returns a <code>DeriveInput</code> struct representing the
parsed Rust code. Listing 19-32 shows the relevant parts of the <code>DeriveInput</code>
struct we get from parsing the <code>struct Pancakes;</code> string:</p>
<pre><code class="language-rust ignore">DeriveInput {
// --snip--
ident: Ident {
ident: &quot;Pancakes&quot;,
span: #0 bytes(95..103)
},
data: Struct(
DataStruct {
struct_token: Struct,
fields: Unit,
semi_token: Some(
Semi
)
}
)
}
</code></pre>
<p><span class="caption">Listing 19-32: The <code>DeriveInput</code> instance we get when
parsing the code that has the macros attribute in Listing 19-30</span></p>
<p>The fields of this struct show that the Rust code weve parsed is a unit struct
with the <code>ident</code> (identifier, meaning the name) of <code>Pancakes</code>. There are more
fields on this struct for describing all sorts of Rust code; check the <a href="https://docs.rs/syn/0.14.4/syn/struct.DeriveInput.html"><code>syn</code>
documentation for <code>DeriveInput</code></a> for more information.</p>
<p>Soon well define the <code>impl_hello_macro</code> function, which is where well build
the new Rust code we want to include. But before we do, note that the output
for our derive macro is also a <code>TokenStream</code>. The returned <code>TokenStream</code> is
added to the code that our crate users write, so when they compile their crate,
theyll get the extra functionality that we provide in the modified
<code>TokenStream</code>.</p>
<p>You might have noticed that were calling <code>unwrap</code> to cause the
<code>hello_macro_derive</code> function to panic if the call to the <code>syn::parse</code> function
fails here. Its necessary for our procedural macro to panic on errors because
<code>proc_macro_derive</code> functions must return <code>TokenStream</code> rather than <code>Result</code> to
conform to the procedural macro API. Weve simplified this example by using
<code>unwrap</code>; in production code, you should provide more specific error messages
about what went wrong by using <code>panic!</code> or <code>expect</code>.</p>
<p>Now that we have the code to turn the annotated Rust code from a <code>TokenStream</code>
into a <code>DeriveInput</code> instance, lets generate the code that implements the
<code>HelloMacro</code> trait on the annotated type, as shown in Listing 19-33.</p>
<p><span class="filename">Filename: hello_macro_derive/src/lib.rs</span></p>
<pre><code class="language-rust ignore">fn impl_hello_macro(ast: &amp;syn::DeriveInput) -&gt; TokenStream {
let name = &amp;ast.ident;
let gen = quote! {
impl HelloMacro for #name {
fn hello_macro() {
println!(&quot;Hello, Macro! My name is {}&quot;, stringify!(#name));
}
}
};
gen.into()
}
</code></pre>
<p><span class="caption">Listing 19-33: Implementing the <code>HelloMacro</code> trait using
the parsed Rust code</span></p>
<p>We get an <code>Ident</code> struct instance containing the name (identifier) of the
annotated type using <code>ast.ident</code>. The struct in Listing 19-32 shows that when
we run the <code>impl_hello_macro</code> function on the code in Listing 19-30, the
<code>ident</code> we get will have the <code>ident</code> field with a value of <code>&quot;Pancakes&quot;</code>. Thus,
the <code>name</code> variable in Listing 19-33 will contain an <code>Ident</code> struct instance
that, when printed, will be the string <code>&quot;Pancakes&quot;</code>, the name of the struct in
Listing 19-30.</p>
<p>The <code>quote!</code> macro lets us define the Rust code that we want to return. The
compiler expects something different to the direct result of the <code>quote!</code>
macros execution, so we need to convert it to a <code>TokenStream</code>. We do this by
calling the <code>into</code> method, which consumes this intermediate representation and
returns a value of the required <code>TokenStream</code> type.</p>
<p>The <code>quote!</code> macro also provides some very cool templating mechanics: we can
enter <code>#name</code>, and <code>quote!</code> will replace it with the value in the variable
<code>name</code>. You can even do some repetition similar to the way regular macros work.
Check out <a href="https://docs.rs/quote">the <code>quote</code> crates docs</a> for a thorough introduction.</p>
<p>We want our procedural macro to generate an implementation of our <code>HelloMacro</code>
trait for the type the user annotated, which we can get by using <code>#name</code>. The
trait implementation has one function, <code>hello_macro</code>, whose body contains the
functionality we want to provide: printing <code>Hello, Macro! My name is</code> and then
the name of the annotated type.</p>
<p>The <code>stringify!</code> macro used here is built into Rust. It takes a Rust
expression, such as <code>1 + 2</code>, and at compile time turns the expression into a
string literal, such as <code>&quot;1 + 2&quot;</code>. This is different than <code>format!</code> or
<code>println!</code>, macros which evaluate the expression and then turn the result into
a <code>String</code>. There is a possibility that the <code>#name</code> input might be an
expression to print literally, so we use <code>stringify!</code>. Using <code>stringify!</code> also
saves an allocation by converting <code>#name</code> to a string literal at compile time.</p>
<p>At this point, <code>cargo build</code> should complete successfully in both <code>hello_macro</code>
and <code>hello_macro_derive</code>. Lets hook up these crates to the code in Listing
19-30 to see the procedural macro in action! Create a new binary project in
your <em>projects</em> directory using <code>cargo new pancakes</code>. We need to add
<code>hello_macro</code> and <code>hello_macro_derive</code> as dependencies in the <code>pancakes</code>
crates <em>Cargo.toml</em>. If youre publishing your versions of <code>hello_macro</code> and
<code>hello_macro_derive</code> to <a href="https://crates.io/">crates.io</a>, they would be regular
dependencies; if not, you can specify them as <code>path</code> dependencies as follows:</p>
<pre><code class="language-toml">[dependencies]
hello_macro = { path = &quot;../hello_macro&quot; }
hello_macro_derive = { path = &quot;../hello_macro/hello_macro_derive&quot; }
</code></pre>
<p>Put the code in Listing 19-30 into <em>src/main.rs</em>, and run <code>cargo run</code>: it
should print <code>Hello, Macro! My name is Pancakes!</code> The implementation of the
<code>HelloMacro</code> trait from the procedural macro was included without the
<code>pancakes</code> crate needing to implement it; the <code>#[derive(HelloMacro)]</code> added the
trait implementation.</p>
<p>Next, lets explore how the other kinds of procedural macros differ from custom
derive macros.</p>
<h3><a class="header" href="#attribute-like-macros" id="attribute-like-macros">Attribute-like macros</a></h3>
<p>Attribute-like macros are similar to custom derive macros, but instead of
generating code for the <code>derive</code> attribute, they allow you to create new
attributes. Theyre also more flexible: <code>derive</code> only works for structs and
enums; attributes can be applied to other items as well, such as functions.
Heres an example of using an attribute-like macro: say you have an attribute
named <code>route</code> that annotates functions when using a web application framework:</p>
<pre><code class="language-rust ignore">#[route(GET, &quot;/&quot;)]
fn index() {
</code></pre>
<p>This <code>#[route]</code> attribute would be defined by the framework as a procedural
macro. The signature of the macro definition function would look like this:</p>
<pre><code class="language-rust ignore">#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -&gt; TokenStream {
</code></pre>
<p>Here, we have two parameters of type <code>TokenStream</code>. The first is for the
contents of the attribute: the <code>GET, &quot;/&quot;</code> part. The second is the body of the
item the attribute is attached to: in this case, <code>fn index() {}</code> and the rest
of the functions body.</p>
<p>Other than that, attribute-like macros work the same way as custom derive
macros: you create a crate with the <code>proc-macro</code> crate type and implement a
function that generates the code you want!</p>
<h3><a class="header" href="#function-like-macros" id="function-like-macros">Function-like macros</a></h3>
<p>Function-like macros define macros that look like function calls. Similarly to
<code>macro_rules!</code> macros, theyre more flexible than functions; for example, they
can take an unknown number of arguments. However, <code>macro_rules!</code> macros can be
defined only using the match-like syntax we discussed in the section
<a href="#declarative-macros-with-macro_rules-for-general-metaprogramming">“Declarative Macros with <code>macro_rules!</code> for General Metaprogramming”</a>
earlier. Function-like macros take a <code>TokenStream</code> parameter and their
definition manipulates that <code>TokenStream</code> using Rust code as the other two
types of procedural macros do. An example of a function-like macro is an <code>sql!</code>
macro that might be called like so:</p>
<pre><code class="language-rust ignore">let sql = sql!(SELECT * FROM posts WHERE id=1);
</code></pre>
<p>This macro would parse the SQL statement inside it and check that its
syntactically correct, which is much more complex processing than a
<code>macro_rules!</code> macro can do. The <code>sql!</code> macro would be defined like this:</p>
<pre><code class="language-rust ignore">#[proc_macro]
pub fn sql(input: TokenStream) -&gt; TokenStream {
</code></pre>
<p>This definition is similar to the custom derive macros signature: we receive
the tokens that are inside the parentheses and return the code we wanted to
generate.</p>
<h2><a class="header" href="#summary" id="summary">Summary</a></h2>
<p>Whew! Now you have some Rust features 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.</p>
<p>Next, well put everything weve discussed throughout the book into practice
and do one more project!</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="ch19-05-advanced-functions-and-closures.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="ch20-00-final-project-a-web-server.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a href="ch19-05-advanced-functions-and-closures.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a href="ch20-00-final-project-a-web-server.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script type="text/javascript">
window.playpen_copyable = true;
</script>
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="book.js" type="text/javascript" charset="utf-8"></script>
<!-- Custom JS scripts -->
<script type="text/javascript" src="ferris.js"></script>
</body>
</html>