From be8aef9cfb19f48225437947868a8cecc7d473f7 Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Tue, 21 Jan 2025 16:29:53 -0700 Subject: [PATCH] Redirects: get rid of the weird gap in Ch. 20 sections! Bonus: fix internal *numbering* references in several places where I had missed it when renumbering the links for the new Ch. 17. --- 2018-edition/src/ch19-03-advanced-traits.md | 2 +- 2018-edition/src/ch19-04-advanced-types.md | 2 +- ...ch19-05-advanced-functions-and-closures.md | 2 +- 2018-edition/src/ch19-06-macros.md | 2 +- book.toml | 12 +- first-edition/src/associated-types.md | 2 +- first-edition/src/macros.md | 2 +- .../src/operators-and-overloading.md | 2 +- first-edition/src/procedural-macros.md | 2 +- first-edition/src/type-aliases.md | 2 +- first-edition/src/unsized-types.md | 2 +- redirects/associated-types.md | 2 +- redirects/macros.md | 2 +- redirects/match.md | 2 +- redirects/operators-and-overloading.md | 2 +- redirects/procedural-macros.md | 4 +- redirects/trait-objects.md | 2 +- redirects/traits.md | 4 +- redirects/type-aliases.md | 2 +- redirects/ufcs.md | 2 +- redirects/unsafe.md | 2 +- redirects/unsized-types.md | 2 +- second-edition/src/appendix-04-macros.md | 2 +- second-edition/src/ch19-03-advanced-traits.md | 2 +- second-edition/src/ch19-04-advanced-types.md | 2 +- ...ch19-05-advanced-functions-and-closures.md | 2 +- src/SUMMARY.md | 8 +- src/appendix-03-derivable-traits.md | 2 +- src/ch18-02-trait-objects.md | 2 +- src/ch18-03-oo-design-patterns.md | 2 +- src/ch20-02-advanced-traits.md | 460 ++++++++++++++++ src/ch20-03-advanced-traits.md | 461 +--------------- src/ch20-03-advanced-types.md | 298 ++++++++++ ...ch20-04-advanced-functions-and-closures.md | 127 +++++ src/ch20-04-advanced-types.md | 299 +--------- ...ch20-05-advanced-functions-and-closures.md | 128 +---- src/ch20-05-macros.md | 514 +++++++++++++++++ src/ch20-06-macros.md | 515 +----------------- src/ch21-02-multithreaded.md | 2 +- 39 files changed, 1446 insertions(+), 1438 deletions(-) create mode 100644 src/ch20-02-advanced-traits.md create mode 100644 src/ch20-03-advanced-types.md create mode 100644 src/ch20-04-advanced-functions-and-closures.md create mode 100644 src/ch20-05-macros.md diff --git a/2018-edition/src/ch19-03-advanced-traits.md b/2018-edition/src/ch19-03-advanced-traits.md index 88ca1a55b4..94f578ae28 100644 --- a/2018-edition/src/ch19-03-advanced-traits.md +++ b/2018-edition/src/ch19-03-advanced-traits.md @@ -3,7 +3,7 @@ The 2018 edition of the book is no longer distributed with Rust's documentation. If you came here via a link or web search, you may want to check out [the current -version of the book](../ch20-03-advanced-traits.html) instead. +version of the book](../ch20-02-advanced-traits.html) instead. If you have an internet connection, you can [find a copy distributed with Rust diff --git a/2018-edition/src/ch19-04-advanced-types.md b/2018-edition/src/ch19-04-advanced-types.md index c7c24439f9..7628f68b42 100644 --- a/2018-edition/src/ch19-04-advanced-types.md +++ b/2018-edition/src/ch19-04-advanced-types.md @@ -3,7 +3,7 @@ The 2018 edition of the book is no longer distributed with Rust's documentation. If you came here via a link or web search, you may want to check out [the current -version of the book](../ch20-04-advanced-types.html) instead. +version of the book](../ch20-03-advanced-types.html) instead. If you have an internet connection, you can [find a copy distributed with Rust diff --git a/2018-edition/src/ch19-05-advanced-functions-and-closures.md b/2018-edition/src/ch19-05-advanced-functions-and-closures.md index c46fd485e1..d31f20a62f 100644 --- a/2018-edition/src/ch19-05-advanced-functions-and-closures.md +++ b/2018-edition/src/ch19-05-advanced-functions-and-closures.md @@ -3,7 +3,7 @@ The 2018 edition of the book is no longer distributed with Rust's documentation. If you came here via a link or web search, you may want to check out [the current -version of the book](../ch20-05-advanced-functions-and-closures.html) instead. +version of the book](../ch20-04-advanced-functions-and-closures.html) instead. If you have an internet connection, you can [find a copy distributed with Rust diff --git a/2018-edition/src/ch19-06-macros.md b/2018-edition/src/ch19-06-macros.md index 838a0b8328..cd92908bb0 100644 --- a/2018-edition/src/ch19-06-macros.md +++ b/2018-edition/src/ch19-06-macros.md @@ -3,7 +3,7 @@ The 2018 edition of the book is no longer distributed with Rust's documentation. If you came here via a link or web search, you may want to check out [the current -version of the book](../ch20-06-macros.html) instead. +version of the book](../ch20-05-macros.html) instead. If you have an internet connection, you can [find a copy distributed with Rust diff --git a/book.toml b/book.toml index 491916a1cc..a17483878f 100644 --- a/book.toml +++ b/book.toml @@ -21,10 +21,14 @@ git-repository-url = "https://github.com/rust-lang/book" "ch18-03-pattern-syntax.html" = "ch19-03-pattern-syntax.html" "ch19-00-advanced-features.html" = "ch20-00-advanced-features.html" "ch19-01-unsafe-rust.html" = "ch20-01-unsafe-rust.html" -"ch19-03-advanced-traits.html" = "ch20-03-advanced-traits.html" -"ch19-04-advanced-types.html" = "ch20-04-advanced-types.html" -"ch19-05-advanced-functions-and-closures.html" = "ch20-05-advanced-functions-and-closures.html" -"ch19-06-macros.html" = "ch20-06-macros.html" +"ch19-03-advanced-traits.html" = "ch20-02-advanced-traits.html" +"ch20-03-advanced-traits.html" = "ch20-02-advanced-traits.html" +"ch19-04-advanced-types.html" = "ch20-03-advanced-types.html" +"ch20-04-advanced-types.html" = "ch20-03-advanced-types.html" +"ch19-05-advanced-functions-and-closures.html" = "ch20-04-advanced-functions-and-closures.html" +"ch20-05-advanced-functions-and-closures.html" = "ch20-04-advanced-functions-and-closures.html" +"ch19-06-macros.html" = "ch20-05-macros.html" +"ch20-06-macros.html" = "ch20-05-macros.html" "ch20-00-final-project-a-web-server.html" = "ch21-00-final-project-a-web-server.html" "ch20-01-single-threaded.html" = "ch21-01-single-threaded.html" "ch20-02-multithreaded.html" = "ch21-02-multithreaded.html" diff --git a/first-edition/src/associated-types.md b/first-edition/src/associated-types.md index be78034984..69340a5392 100644 --- a/first-edition/src/associated-types.md +++ b/first-edition/src/associated-types.md @@ -3,7 +3,7 @@ The first edition of the book is no longer distributed with Rust's documentation. If you came here via a link or web search, you may want to check out [the current -version of the book](../ch20-03-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types) instead. +version of the book](../ch20-02-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types) instead. If you have an internet connection, you can [find a copy distributed with Rust diff --git a/first-edition/src/macros.md b/first-edition/src/macros.md index eb9871ca4a..587f0c79b1 100644 --- a/first-edition/src/macros.md +++ b/first-edition/src/macros.md @@ -3,7 +3,7 @@ The first edition of the book is no longer distributed with Rust's documentation. If you came here via a link or web search, you may want to check out [the current -version of the book](../ch20-06-macros.html) instead. +version of the book](../ch20-05-macros.html) instead. If you have an internet connection, you can [find a copy distributed with Rust diff --git a/first-edition/src/operators-and-overloading.md b/first-edition/src/operators-and-overloading.md index a13f444376..2b4eecfbd4 100644 --- a/first-edition/src/operators-and-overloading.md +++ b/first-edition/src/operators-and-overloading.md @@ -3,7 +3,7 @@ The first edition of the book is no longer distributed with Rust's documentation. If you came here via a link or web search, you may want to check out [the current -version of the book](../ch20-03-advanced-traits.html#default-generic-type-parameters-and-operator-overloading) instead. +version of the book](../ch20-02-advanced-traits.html#default-generic-type-parameters-and-operator-overloading) instead. If you have an internet connection, you can [find a copy distributed with Rust diff --git a/first-edition/src/procedural-macros.md b/first-edition/src/procedural-macros.md index 3b683a9759..b52a43d748 100644 --- a/first-edition/src/procedural-macros.md +++ b/first-edition/src/procedural-macros.md @@ -3,7 +3,7 @@ The first edition of the book is no longer distributed with Rust's documentation. If you came here via a link or web search, you may want to check out [the current -version of the book](../ch20-06-macros.html?highlight=procedural#procedural-macros-for-generating-code-from-attributes) instead. +version of the book](../ch20-05-macros.html?highlight=procedural#procedural-macros-for-generating-code-from-attributes) instead. If you have an internet connection, you can [find a copy distributed with Rust diff --git a/first-edition/src/type-aliases.md b/first-edition/src/type-aliases.md index d79974e550..8e9bde0f11 100644 --- a/first-edition/src/type-aliases.md +++ b/first-edition/src/type-aliases.md @@ -3,7 +3,7 @@ The first edition of the book is no longer distributed with Rust's documentation. If you came here via a link or web search, you may want to check out [the current -version of the book](../ch20-04-advanced-types.html#creating-type-synonyms-with-type-aliases) instead. +version of the book](../ch20-03-advanced-types.html#creating-type-synonyms-with-type-aliases) instead. If you have an internet connection, you can [find a copy distributed with Rust diff --git a/first-edition/src/unsized-types.md b/first-edition/src/unsized-types.md index 5fb51b1fd2..cc16741f9c 100644 --- a/first-edition/src/unsized-types.md +++ b/first-edition/src/unsized-types.md @@ -3,7 +3,7 @@ The first edition of the book is no longer distributed with Rust's documentation. If you came here via a link or web search, you may want to check out [the current -version of the book](../ch20-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait) instead. +version of the book](../ch20-03-advanced-types.html#dynamically-sized-types-and-the-sized-trait) instead. If you have an internet connection, you can [find a copy distributed with Rust diff --git a/redirects/associated-types.md b/redirects/associated-types.md index af052c160d..f91fc23d6f 100644 --- a/redirects/associated-types.md +++ b/redirects/associated-types.md @@ -14,4 +14,4 @@ pub trait Iterator { --- You can find the latest version of this information -[here](ch20-03-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types). +[here](ch20-02-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types). diff --git a/redirects/macros.md b/redirects/macros.md index 828d484dd6..f8ada9abf9 100644 --- a/redirects/macros.md +++ b/redirects/macros.md @@ -18,7 +18,7 @@ fn main() { Here are the relevant sections in the new and old books: -* **[In the current edition: Ch 19.06 Macros][2]** +* **[In the current edition: Ch 20.05 Macros][2]** * [Rust By Example: Macros][3] * [In the Rust Reference: Ch 3.1 — Macros by Example][4] * [In the first edition: Ch 3.34 — Macros][1] diff --git a/redirects/match.md b/redirects/match.md index 5bcbc89495..c76ffadb7a 100644 --- a/redirects/match.md +++ b/redirects/match.md @@ -28,7 +28,7 @@ fn value_in_cents(coin: Coin) -> u32 { Here are the relevant sections in the new and old books: * **[in the current edition: Ch 6.02 — The `match` Control Flow Operator][2]** -* [in the current edition: Ch 18.00 — Patterns][3] +* [in the current edition: Ch 19.00 — Patterns][3] * [In the first edition: Ch 3.14 — Match][1] diff --git a/redirects/operators-and-overloading.md b/redirects/operators-and-overloading.md index 3a61fbabc4..b77cf7c913 100644 --- a/redirects/operators-and-overloading.md +++ b/redirects/operators-and-overloading.md @@ -33,4 +33,4 @@ fn main() { --- You can find the latest version of this information -[here](ch20-03-advanced-traits.html). +[here](ch20-02-advanced-traits.html). diff --git a/redirects/procedural-macros.md b/redirects/procedural-macros.md index 07657f6c88..7c72392b70 100644 --- a/redirects/procedural-macros.md +++ b/redirects/procedural-macros.md @@ -9,13 +9,13 @@ This chapter does not exist yet in [the second edition][2]. You can check out other resources that describe macros. -* **[In the current edition: Ch 19.06 Macros][2]** +* **[In the current edition: Ch 20.05 Macros][2]** * [In the Rust Reference: Ch 3.2 — Procedural Macros][4] * [The `proc_macro` crate documentation][3] * [In the first edition: Ch 4.13 — Procedural Macros (and custom Derive)][1] [1]: https://doc.rust-lang.org/1.30.0/book/first-edition/procedural-macros.html -[2]: ch20-06-macros.html +[2]: ch20-05-macros.html [3]: ../proc_macro/index.html [4]: ../reference/procedural-macros.html diff --git a/redirects/trait-objects.md b/redirects/trait-objects.md index 44ca328235..a0e7438d45 100644 --- a/redirects/trait-objects.md +++ b/redirects/trait-objects.md @@ -60,7 +60,7 @@ fn main() { Here are the relevant sections in the new and old books: -* **[in the current edition: Ch 17.02 — Trait Objects][2]** +* **[in the current edition: Ch 18.02 — Trait Objects][2]** * [In the first edition: Ch 3.22 — Trait Objects][1] diff --git a/redirects/traits.md b/redirects/traits.md index b36ede6640..9f883c30f5 100644 --- a/redirects/traits.md +++ b/redirects/traits.md @@ -15,10 +15,10 @@ pub trait Summarizable { Here are the relevant sections in the new and old books: * **[in the current edition: Ch 10.02 — Traits][2]** -* [in the current edition: Ch 19.03 — Advanced Traits][3] +* [in the current edition: Ch 20.03 — Advanced Traits][3] * [In the first edition: Ch 3.19 — Traits][1] [1]: https://doc.rust-lang.org/1.30.0/book/first-edition/traits.html [2]: ch10-02-traits.html -[3]: ch20-03-advanced-traits.html +[3]: ch20-02-advanced-traits.html diff --git a/redirects/type-aliases.md b/redirects/type-aliases.md index 84f94e41e9..986e7b9e33 100644 --- a/redirects/type-aliases.md +++ b/redirects/type-aliases.md @@ -11,4 +11,4 @@ type Kilometers = i32; --- You can find the latest version of this information -[here](ch20-04-advanced-types.html#creating-type-synonyms-with-type-aliases). +[here](ch20-03-advanced-types.html#creating-type-synonyms-with-type-aliases). diff --git a/redirects/ufcs.md b/redirects/ufcs.md index 684c02ec77..383465398b 100644 --- a/redirects/ufcs.md +++ b/redirects/ufcs.md @@ -45,4 +45,4 @@ fn main() { --- You can find the latest version of this information -[here](ch20-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name). +[here](ch20-02-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name). diff --git a/redirects/unsafe.md b/redirects/unsafe.md index 94ee8ad777..7d9adb39d8 100644 --- a/redirects/unsafe.md +++ b/redirects/unsafe.md @@ -8,7 +8,7 @@ Here are the relevant sections in the new and old books: -* **[in the current edition: Ch 19.01 — Unsafe Rust][2]** +* **[in the current edition: Ch 20.01 — Unsafe Rust][2]** * [The Rustonomicon, The Dark Arts of Advanced and Unsafe Rust Programming][3] * [In the first edition: Ch 3.36 — `unsafe`][1] diff --git a/redirects/unsized-types.md b/redirects/unsized-types.md index 6e8d19da4a..a9ca1e03e0 100644 --- a/redirects/unsized-types.md +++ b/redirects/unsized-types.md @@ -15,4 +15,4 @@ fn generic(t: &T) { --- You can find the latest version of this information -[here](ch20-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait). +[here](ch20-03-advanced-types.html#dynamically-sized-types-and-the-sized-trait). diff --git a/second-edition/src/appendix-04-macros.md b/second-edition/src/appendix-04-macros.md index dfcdcec07c..f04f9eb4e8 100644 --- a/second-edition/src/appendix-04-macros.md +++ b/second-edition/src/appendix-04-macros.md @@ -3,7 +3,7 @@ The second edition of the book is no longer distributed with Rust's documentation. If you came here via a link or web search, you may want to check out [the current -version of the book](../ch20-06-macros.html) instead. +version of the book](../ch20-05-macros.html) instead. If you have an internet connection, you can [find a copy distributed with Rust diff --git a/second-edition/src/ch19-03-advanced-traits.md b/second-edition/src/ch19-03-advanced-traits.md index 56b9c7e662..07c9d71a54 100644 --- a/second-edition/src/ch19-03-advanced-traits.md +++ b/second-edition/src/ch19-03-advanced-traits.md @@ -3,7 +3,7 @@ The second edition of the book is no longer distributed with Rust's documentation. If you came here via a link or web search, you may want to check out [the current -version of the book](../ch20-03-advanced-traits.html) instead. +version of the book](../ch20-02-advanced-traits.html) instead. If you have an internet connection, you can [find a copy distributed with Rust diff --git a/second-edition/src/ch19-04-advanced-types.md b/second-edition/src/ch19-04-advanced-types.md index c217a12b5a..153ad9cf44 100644 --- a/second-edition/src/ch19-04-advanced-types.md +++ b/second-edition/src/ch19-04-advanced-types.md @@ -3,7 +3,7 @@ The second edition of the book is no longer distributed with Rust's documentation. If you came here via a link or web search, you may want to check out [the current -version of the book](../ch20-04-advanced-types.html) instead. +version of the book](../ch20-03-advanced-types.html) instead. If you have an internet connection, you can [find a copy distributed with Rust diff --git a/second-edition/src/ch19-05-advanced-functions-and-closures.md b/second-edition/src/ch19-05-advanced-functions-and-closures.md index 6c296b9efe..2d8b64c07d 100644 --- a/second-edition/src/ch19-05-advanced-functions-and-closures.md +++ b/second-edition/src/ch19-05-advanced-functions-and-closures.md @@ -3,7 +3,7 @@ The second edition of the book is no longer distributed with Rust's documentation. If you came here via a link or web search, you may want to check out [the current -version of the book](../ch20-05-advanced-functions-and-closures.html) instead. +version of the book](../ch20-04-advanced-functions-and-closures.html) instead. If you have an internet connection, you can [find a copy distributed with Rust diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 5a83bd0b00..683b997667 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -123,10 +123,10 @@ - [Advanced Features](ch20-00-advanced-features.md) - [Unsafe Rust](ch20-01-unsafe-rust.md) - - [Advanced Traits](ch20-03-advanced-traits.md) - - [Advanced Types](ch20-04-advanced-types.md) - - [Advanced Functions and Closures](ch20-05-advanced-functions-and-closures.md) - - [Macros](ch20-06-macros.md) + - [Advanced Traits](ch20-02-advanced-traits.md) + - [Advanced Types](ch20-03-advanced-types.md) + - [Advanced Functions and Closures](ch20-04-advanced-functions-and-closures.md) + - [Macros](ch20-05-macros.md) - [Final Project: Building a Multithreaded Web Server](ch21-00-final-project-a-web-server.md) - [Building a Single-Threaded Web Server](ch21-01-single-threaded.md) diff --git a/src/appendix-03-derivable-traits.md b/src/appendix-03-derivable-traits.md index 2365ade130..c1839b276f 100644 --- a/src/appendix-03-derivable-traits.md +++ b/src/appendix-03-derivable-traits.md @@ -181,4 +181,4 @@ The `Default` trait is required when you use the method `unwrap_or_default` on [creating-instances-from-other-instances-with-struct-update-syntax]: ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax [stack-only-data-copy]: ch04-01-what-is-ownership.html#stack-only-data-copy [ways-variables-and-data-interact-clone]: ch04-01-what-is-ownership.html#ways-variables-and-data-interact-clone -[macros]: ch20-06-macros.html#macros +[macros]: ch20-05-macros.html#macros diff --git a/src/ch18-02-trait-objects.md b/src/ch18-02-trait-objects.md index 980b1206c7..db118ef223 100644 --- a/src/ch18-02-trait-objects.md +++ b/src/ch18-02-trait-objects.md @@ -245,5 +245,5 @@ that we wrote in Listing 18-5 and were able to support in Listing 18-9, so it’ a trade-off to consider. [performance-of-code-using-generics]: ch10-01-syntax.html#performance-of-code-using-generics -[dynamically-sized]: ch20-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait +[dynamically-sized]: ch20-03-advanced-types.html#dynamically-sized-types-and-the-sized-trait [dyn-compatibility]: https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility diff --git a/src/ch18-03-oo-design-patterns.md b/src/ch18-03-oo-design-patterns.md index 8f50a56fdb..e46f34d82a 100644 --- a/src/ch18-03-oo-design-patterns.md +++ b/src/ch18-03-oo-design-patterns.md @@ -503,4 +503,4 @@ lots of flexibility. We’ve looked at them briefly throughout the book but haven’t seen their full capability yet. Let’s go! [more-info-than-rustc]: ch09-03-to-panic-or-not-to-panic.html#cases-in-which-you-have-more-information-than-the-compiler -[macros]: ch20-06-macros.html#macros +[macros]: ch20-05-macros.html#macros diff --git a/src/ch20-02-advanced-traits.md b/src/ch20-02-advanced-traits.md new file mode 100644 index 0000000000..ebb9fff0a8 --- /dev/null +++ b/src/ch20-02-advanced-traits.md @@ -0,0 +1,460 @@ +## Advanced Traits + +We first covered traits in the [“Traits: Defining Shared +Behavior”][traits-defining-shared-behavior] section of Chapter +10, but we didn’t discuss the more advanced details. Now that you know more +about Rust, we can get into the nitty-gritty. + +### Specifying Placeholder Types in Trait Definitions with Associated Types + +_Associated types_ connect a type placeholder with a trait such that the trait +method definitions can use these placeholder types in their signatures. The +implementor of a trait will specify the concrete type to be used instead of the +placeholder type for the particular implementation. That way, we can define a +trait that uses some types without needing to know exactly what those types are +until the trait is implemented. + +We’ve described most of the advanced features in this chapter as being rarely +needed. Associated types are somewhere in the middle: they’re used more rarely +than features explained in the rest of the book but more commonly than many of +the other features discussed in this chapter. + +One example of a trait with an associated type is the `Iterator` trait that the +standard library provides. The associated type is named `Item` and stands in +for the type of the values the type implementing the `Iterator` trait is +iterating over. The definition of the `Iterator` trait is as shown in Listing +20-13. + ++ +```rust,noplayground +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-13/src/lib.rs}} +``` + + + +The type `Item` is a placeholder, and the `next` method’s definition shows that +it will return values of type `Option`. Implementors of the +`Iterator` trait will specify the concrete type for `Item`, and the `next` +method will return an `Option` containing a value of that concrete type. + +Associated types might seem like a similar concept to generics, in that the +latter allow us to define a function without specifying what types it can +handle. To examine the difference between the two concepts, we’ll look at an +implementation of the `Iterator` trait on a type named `Counter` that specifies +the `Item` type is `u32`: + ++ +```rust,ignore +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-22-iterator-on-counter/src/lib.rs:ch19}} +``` + + + +This syntax seems comparable to that of generics. So why not just define the +`Iterator` trait with generics, as shown in Listing 20-14? + ++ +```rust,noplayground +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-14/src/lib.rs}} +``` + + + +The difference is that when using generics, as in Listing 20-14, we must +annotate the types in each implementation; because we can also implement +`Iterator for Counter` or any other type, we could have multiple +implementations of `Iterator` for `Counter`. In other words, when a trait has a +generic parameter, it can be implemented for a type multiple times, changing +the concrete types of the generic type parameters each time. When we use the +`next` method on `Counter`, we would have to provide type annotations to +indicate which implementation of `Iterator` we want to use. + +With associated types, we don’t need to annotate types because we can’t +implement a trait on a type multiple times. In Listing 20-13 with the +definition that uses associated types, we can only choose what the type of +`Item` will be once, because there can only be one `impl Iterator for Counter`. +We don’t have to specify that we want an iterator of `u32` values everywhere +that we call `next` on `Counter`. + +Associated types also become part of the trait’s contract: implementors of the +trait must provide a type to stand in for the associated type placeholder. +Associated types often have a name that describes how the type will be used, +and documenting the associated type in the API documentation is good practice. + +### Default Generic Type Parameters and Operator Overloading + +When we use generic type parameters, we can specify a default concrete type for +the generic type. This eliminates the need for implementors of the trait to +specify a concrete type if the default type works. You specify a default type +when declaring a generic type with the `` syntax. + +A great example of a situation where this technique is useful is with _operator +overloading_, in which you customize the behavior of an operator (such as `+`) +in particular situations. + +Rust doesn’t allow you to create your own operators or overload arbitrary +operators. But you can overload the operations and corresponding traits listed +in `std::ops` by implementing the traits associated with the operator. For +example, in Listing 20-15 we overload the `+` operator to add two `Point` +instances together. We do this by implementing the `Add` trait on a `Point` +struct: + ++ +```rust +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-15/src/main.rs}} +``` + + + +The `add` method adds the `x` values of two `Point` instances and the `y` +values of two `Point` instances to create a new `Point`. The `Add` trait has an +associated type named `Output` that determines the type returned from the `add` +method. + +The default generic type in this code is within the `Add` trait. Here is its +definition: + +```rust +trait Add { + type Output; + + fn add(self, rhs: Rhs) -> Self::Output; +} +``` + +This code should look generally familiar: a trait with one method and an +associated type. The new part is `Rhs=Self`: this syntax is called _default +type parameters_. The `Rhs` generic type parameter (short for “right hand +side”) defines the type of the `rhs` parameter in the `add` method. If we don’t +specify a concrete type for `Rhs` when we implement the `Add` trait, the type +of `Rhs` will default to `Self`, which will be the type we’re implementing +`Add` on. + +When we implemented `Add` for `Point`, we used the default for `Rhs` because we +wanted to add two `Point` instances. Let’s look at an example of implementing +the `Add` trait where we want to customize the `Rhs` type rather than using the +default. + +We have two structs, `Millimeters` and `Meters`, holding values in different +units. This thin wrapping of an existing type in another struct is known as the +_newtype pattern_, which we describe in more detail in the [“Using the Newtype +Pattern to Implement External Traits on External Types”][newtype] section. We want to add values in millimeters to values in meters and have +the implementation of `Add` do the conversion correctly. We can implement `Add` +for `Millimeters` with `Meters` as the `Rhs`, as shown in Listing 20-16. + ++ +```rust,noplayground +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-16/src/lib.rs}} +``` + + + +To add `Millimeters` and `Meters`, we specify `impl Add` to set the +value of the `Rhs` type parameter instead of using the default of `Self`. + +You’ll use default type parameters in two main ways: + +- To extend a type without breaking existing code +- To allow customization in specific cases most users won’t need + +The standard library’s `Add` trait is an example of the second purpose: +usually, you’ll add two like types, but the `Add` trait provides the ability to +customize beyond that. Using a default type parameter in the `Add` trait +definition means you don’t have to specify the extra parameter most of the +time. In other words, a bit of implementation boilerplate isn’t needed, making +it easier to use the trait. + +The first purpose is similar to the second but in reverse: if you want to add a +type parameter to an existing trait, you can give it a default to allow +extension of the functionality of the trait without breaking the existing +implementation code. + +### Fully Qualified Syntax for Disambiguation: Calling Methods with the Same Name + +Nothing in Rust prevents a trait from having a method with the same name as +another trait’s method, nor does Rust prevent you from implementing both traits +on one type. It’s also possible to implement a method directly on the type with +the same name as methods from traits. + +When calling methods with the same name, you’ll need to tell Rust which one you +want to use. Consider the code in Listing 20-17 where we’ve defined two traits, +`Pilot` and `Wizard`, that both have a method called `fly`. We then implement +both traits on a type `Human` that already has a method named `fly` implemented +on it. Each `fly` method does something different. + ++ +```rust +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-17/src/main.rs:here}} +``` + + + +When we call `fly` on an instance of `Human`, the compiler defaults to calling +the method that is directly implemented on the type, as shown in Listing 20-18. + ++ +```rust +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-18/src/main.rs:here}} +``` + + + +Running this code will print `*waving arms furiously*`, showing that Rust +called the `fly` method implemented on `Human` directly. + +To call the `fly` methods from either the `Pilot` trait or the `Wizard` trait, +we need to use more explicit syntax to specify which `fly` method we mean. +Listing 20-19 demonstrates this syntax. + ++ +```rust +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-19/src/main.rs:here}} +``` + + + +Specifying the trait name before the method name clarifies to Rust which +implementation of `fly` we want to call. We could also write +`Human::fly(&person)`, which is equivalent to the `person.fly()` that we used +in Listing 20-19, but this is a bit longer to write if we don’t need to +disambiguate. + +Running this code prints the following: + +```console +{{#include ../listings/ch20-advanced-features/listing-20-19/output.txt}} +``` + +Because the `fly` method takes a `self` parameter, if we had two _types_ that +both implement one _trait_, Rust could figure out which implementation of a +trait to use based on the type of `self`. + +However, associated functions that are not methods don’t have a `self` +parameter. When there are multiple types or traits that define non-method +functions with the same function name, Rust doesn't always know which type you +mean unless you use _fully qualified syntax_. For example, in Listing 20-20 we +create a trait for an animal shelter that wants to name all baby dogs _Spot_. +We make an `Animal` trait with an associated non-method function `baby_name`. +The `Animal` trait is implemented for the struct `Dog`, on which we also +provide an associated non-method function `baby_name` directly. + ++ +```rust +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-20/src/main.rs}} +``` + + + +We implement the code for naming all puppies Spot in the `baby_name` associated +function that is defined on `Dog`. The `Dog` type also implements the trait +`Animal`, which describes characteristics that all animals have. Baby dogs are +called puppies, and that is expressed in the implementation of the `Animal` +trait on `Dog` in the `baby_name` function associated with the `Animal` trait. + +In `main`, we call the `Dog::baby_name` function, which calls the associated +function defined on `Dog` directly. This code prints the following: + +```console +{{#include ../listings/ch20-advanced-features/listing-20-20/output.txt}} +``` + +This output isn’t what we wanted. We want to call the `baby_name` function that +is part of the `Animal` trait that we implemented on `Dog` so the code prints +`A baby dog is called a puppy`. The technique of specifying the trait name that +we used in Listing 20-19 doesn’t help here; if we change `main` to the code in +Listing 20-21, we’ll get a compilation error. + ++ +```rust,ignore,does_not_compile +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-21/src/main.rs:here}} +``` + + + +Because `Animal::baby_name` doesn’t have a `self` parameter, and there could be +other types that implement the `Animal` trait, Rust can’t figure out which +implementation of `Animal::baby_name` we want. We’ll get this compiler error: + +```console +{{#include ../listings/ch20-advanced-features/listing-20-21/output.txt}} +``` + +To disambiguate and tell Rust that we want to use the implementation of +`Animal` for `Dog` as opposed to the implementation of `Animal` for some other +type, we need to use fully qualified syntax. Listing 20-22 demonstrates how to +use fully qualified syntax. + ++ +```rust +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-22/src/main.rs:here}} +``` + + + +We’re providing Rust with a type annotation within the angle brackets, which +indicates we want to call the `baby_name` method from the `Animal` trait as +implemented on `Dog` by saying that we want to treat the `Dog` type as an +`Animal` for this function call. This code will now print what we want: + +```console +{{#include ../listings/ch20-advanced-features/listing-20-22/output.txt}} +``` + +In general, fully qualified syntax is defined as follows: + +```rust,ignore +::function(receiver_if_method, next_arg, ...); +``` + +For associated functions that aren’t methods, there would not be a `receiver`: +there would only be the list of other arguments. You could use fully qualified +syntax everywhere that you call functions or methods. However, you’re allowed +to omit any part of this syntax that Rust can figure out from other information +in the program. You only need to use this more verbose syntax in cases where +there are multiple implementations that use the same name and Rust needs help +to identify which implementation you want to call. + +### Using Supertraits to Require One Trait’s Functionality Within Another Trait + +Sometimes, you might write a trait definition that depends on another trait: +for a type to implement the first trait, you want to require that type to also +implement the second trait. You would do this so that your trait definition can +make use of the associated items of the second trait. The trait your trait +definition is relying on is called a _supertrait_ of your trait. + +For example, let’s say we want to make an `OutlinePrint` trait with an +`outline_print` method that will print a given value formatted so that it's +framed in asterisks. That is, given a `Point` struct that implements the +standard library trait `Display` to result in `(x, y)`, when we call +`outline_print` on a `Point` instance that has `1` for `x` and `3` for `y`, it +should print the following: + +```text +********** +* * +* (1, 3) * +* * +********** +``` + +In the implementation of the `outline_print` method, we want to use the +`Display` trait’s functionality. Therefore, we need to specify that the +`OutlinePrint` trait will work only for types that also implement `Display` and +provide the functionality that `OutlinePrint` needs. We can do that in the +trait definition by specifying `OutlinePrint: Display`. This technique is +similar to adding a trait bound to the trait. Listing 20-23 shows an +implementation of the `OutlinePrint` trait. + ++ +```rust +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-23/src/main.rs:here}} +``` + + + +Because we’ve specified that `OutlinePrint` requires the `Display` trait, we +can use the `to_string` function that is automatically implemented for any type +that implements `Display`. If we tried to use `to_string` without adding a +colon and specifying the `Display` trait after the trait name, we’d get an +error saying that no method named `to_string` was found for the type `&Self` in +the current scope. + +Let’s see what happens when we try to implement `OutlinePrint` on a type that +doesn’t implement `Display`, such as the `Point` struct: + ++ +```rust,ignore,does_not_compile +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-02-impl-outlineprint-for-point/src/main.rs:here}} +``` + + + +We get an error saying that `Display` is required but not implemented: + +```console +{{#include ../listings/ch20-advanced-features/no-listing-02-impl-outlineprint-for-point/output.txt}} +``` + +To fix this, we implement `Display` on `Point` and satisfy the constraint that +`OutlinePrint` requires, like so: + ++ +```rust +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-03-impl-display-for-point/src/main.rs:here}} +``` + + + +Then implementing the `OutlinePrint` trait on `Point` will compile +successfully, and we can call `outline_print` on a `Point` instance to display +it within an outline of asterisks. + +### Using the Newtype Pattern to Implement External Traits on External Types + +In Chapter 10 in the [“Implementing a Trait on a +Type”][implementing-a-trait-on-a-type] section, we mentioned the +orphan rule that states we’re only allowed to implement a trait on a type if +either the trait or the type are local to our crate. It’s possible to get +around this restriction using the _newtype pattern_, which involves creating a +new type in a tuple struct. (We covered tuple structs in the [“Using Tuple +Structs without Named Fields to Create Different Types”][tuple-structs] section of Chapter 5.) The tuple struct will have one field and be a +thin wrapper around the type we want to implement a trait for. Then the wrapper +type is local to our crate, and we can implement the trait on the wrapper. +_Newtype_ is a term that originates from the Haskell programming language. +There is no runtime performance penalty for using this pattern, and the wrapper +type is elided at compile time. + +As an example, let’s say we want to implement `Display` on `Vec`, which the +orphan rule prevents us from doing directly because the `Display` trait and the +`Vec` type are defined outside our crate. We can make a `Wrapper` struct +that holds an instance of `Vec`; then we can implement `Display` on +`Wrapper` and use the `Vec` value, as shown in Listing 20-24. + ++ +```rust +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-24/src/main.rs}} +``` + + + +The implementation of `Display` uses `self.0` to access the inner `Vec`, +because `Wrapper` is a tuple struct and `Vec` is the item at index 0 in the +tuple. Then we can use the functionality of the `Display` trait on `Wrapper`. + +The downside of using this technique is that `Wrapper` is a new type, so it +doesn’t have the methods of the value it’s holding. We would have to implement +all the methods of `Vec` directly on `Wrapper` such that the methods +delegate to `self.0`, which would allow us to treat `Wrapper` exactly like a +`Vec`. If we wanted the new type to have every method the inner type has, +implementing the `Deref` trait (discussed in Chapter 15 in the [“Treating Smart +Pointers Like Regular References with the `Deref` +Trait”][smart-pointer-deref] section) on the `Wrapper` to return +the inner type would be a solution. If we don’t want the `Wrapper` type to have +all the methods of the inner type—for example, to restrict the `Wrapper` type’s +behavior—we would have to implement just the methods we do want manually. + +This newtype pattern is also useful even when traits are not involved. Let’s +switch focus and look at some advanced ways to interact with Rust’s type system. + +[newtype]: ch20-02-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types +[implementing-a-trait-on-a-type]: ch10-02-traits.html#implementing-a-trait-on-a-type +[traits-defining-shared-behavior]: ch10-02-traits.html#traits-defining-shared-behavior +[smart-pointer-deref]: ch15-02-deref.html#treating-smart-pointers-like-regular-references-with-the-deref-trait +[tuple-structs]: ch05-01-defining-structs.html#using-tuple-structs-without-named-fields-to-create-different-types diff --git a/src/ch20-03-advanced-traits.md b/src/ch20-03-advanced-traits.md index 58a2ff86ab..d9ce050f47 100644 --- a/src/ch20-03-advanced-traits.md +++ b/src/ch20-03-advanced-traits.md @@ -1,460 +1 @@ -## Advanced Traits - -We first covered traits in the [“Traits: Defining Shared -Behavior”][traits-defining-shared-behavior] section of Chapter -10, but we didn’t discuss the more advanced details. Now that you know more -about Rust, we can get into the nitty-gritty. - -### Specifying Placeholder Types in Trait Definitions with Associated Types - -_Associated types_ connect a type placeholder with a trait such that the trait -method definitions can use these placeholder types in their signatures. The -implementor of a trait will specify the concrete type to be used instead of the -placeholder type for the particular implementation. That way, we can define a -trait that uses some types without needing to know exactly what those types are -until the trait is implemented. - -We’ve described most of the advanced features in this chapter as being rarely -needed. Associated types are somewhere in the middle: they’re used more rarely -than features explained in the rest of the book but more commonly than many of -the other features discussed in this chapter. - -One example of a trait with an associated type is the `Iterator` trait that the -standard library provides. The associated type is named `Item` and stands in -for the type of the values the type implementing the `Iterator` trait is -iterating over. The definition of the `Iterator` trait is as shown in Listing -20-13. - -- -```rust,noplayground -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-13/src/lib.rs}} -``` - - - -The type `Item` is a placeholder, and the `next` method’s definition shows that -it will return values of type `Option`. Implementors of the -`Iterator` trait will specify the concrete type for `Item`, and the `next` -method will return an `Option` containing a value of that concrete type. - -Associated types might seem like a similar concept to generics, in that the -latter allow us to define a function without specifying what types it can -handle. To examine the difference between the two concepts, we’ll look at an -implementation of the `Iterator` trait on a type named `Counter` that specifies -the `Item` type is `u32`: - -- -```rust,ignore -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-22-iterator-on-counter/src/lib.rs:ch19}} -``` - - - -This syntax seems comparable to that of generics. So why not just define the -`Iterator` trait with generics, as shown in Listing 20-14? - -- -```rust,noplayground -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-14/src/lib.rs}} -``` - - - -The difference is that when using generics, as in Listing 20-14, we must -annotate the types in each implementation; because we can also implement -`Iterator for Counter` or any other type, we could have multiple -implementations of `Iterator` for `Counter`. In other words, when a trait has a -generic parameter, it can be implemented for a type multiple times, changing -the concrete types of the generic type parameters each time. When we use the -`next` method on `Counter`, we would have to provide type annotations to -indicate which implementation of `Iterator` we want to use. - -With associated types, we don’t need to annotate types because we can’t -implement a trait on a type multiple times. In Listing 20-13 with the -definition that uses associated types, we can only choose what the type of -`Item` will be once, because there can only be one `impl Iterator for Counter`. -We don’t have to specify that we want an iterator of `u32` values everywhere -that we call `next` on `Counter`. - -Associated types also become part of the trait’s contract: implementors of the -trait must provide a type to stand in for the associated type placeholder. -Associated types often have a name that describes how the type will be used, -and documenting the associated type in the API documentation is good practice. - -### Default Generic Type Parameters and Operator Overloading - -When we use generic type parameters, we can specify a default concrete type for -the generic type. This eliminates the need for implementors of the trait to -specify a concrete type if the default type works. You specify a default type -when declaring a generic type with the `` syntax. - -A great example of a situation where this technique is useful is with _operator -overloading_, in which you customize the behavior of an operator (such as `+`) -in particular situations. - -Rust doesn’t allow you to create your own operators or overload arbitrary -operators. But you can overload the operations and corresponding traits listed -in `std::ops` by implementing the traits associated with the operator. For -example, in Listing 20-15 we overload the `+` operator to add two `Point` -instances together. We do this by implementing the `Add` trait on a `Point` -struct: - -- -```rust -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-15/src/main.rs}} -``` - - - -The `add` method adds the `x` values of two `Point` instances and the `y` -values of two `Point` instances to create a new `Point`. The `Add` trait has an -associated type named `Output` that determines the type returned from the `add` -method. - -The default generic type in this code is within the `Add` trait. Here is its -definition: - -```rust -trait Add { - type Output; - - fn add(self, rhs: Rhs) -> Self::Output; -} -``` - -This code should look generally familiar: a trait with one method and an -associated type. The new part is `Rhs=Self`: this syntax is called _default -type parameters_. The `Rhs` generic type parameter (short for “right hand -side”) defines the type of the `rhs` parameter in the `add` method. If we don’t -specify a concrete type for `Rhs` when we implement the `Add` trait, the type -of `Rhs` will default to `Self`, which will be the type we’re implementing -`Add` on. - -When we implemented `Add` for `Point`, we used the default for `Rhs` because we -wanted to add two `Point` instances. Let’s look at an example of implementing -the `Add` trait where we want to customize the `Rhs` type rather than using the -default. - -We have two structs, `Millimeters` and `Meters`, holding values in different -units. This thin wrapping of an existing type in another struct is known as the -_newtype pattern_, which we describe in more detail in the [“Using the Newtype -Pattern to Implement External Traits on External Types”][newtype] section. We want to add values in millimeters to values in meters and have -the implementation of `Add` do the conversion correctly. We can implement `Add` -for `Millimeters` with `Meters` as the `Rhs`, as shown in Listing 20-16. - -- -```rust,noplayground -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-16/src/lib.rs}} -``` - - - -To add `Millimeters` and `Meters`, we specify `impl Add` to set the -value of the `Rhs` type parameter instead of using the default of `Self`. - -You’ll use default type parameters in two main ways: - -- To extend a type without breaking existing code -- To allow customization in specific cases most users won’t need - -The standard library’s `Add` trait is an example of the second purpose: -usually, you’ll add two like types, but the `Add` trait provides the ability to -customize beyond that. Using a default type parameter in the `Add` trait -definition means you don’t have to specify the extra parameter most of the -time. In other words, a bit of implementation boilerplate isn’t needed, making -it easier to use the trait. - -The first purpose is similar to the second but in reverse: if you want to add a -type parameter to an existing trait, you can give it a default to allow -extension of the functionality of the trait without breaking the existing -implementation code. - -### Fully Qualified Syntax for Disambiguation: Calling Methods with the Same Name - -Nothing in Rust prevents a trait from having a method with the same name as -another trait’s method, nor does Rust prevent you from implementing both traits -on one type. It’s also possible to implement a method directly on the type with -the same name as methods from traits. - -When calling methods with the same name, you’ll need to tell Rust which one you -want to use. Consider the code in Listing 20-17 where we’ve defined two traits, -`Pilot` and `Wizard`, that both have a method called `fly`. We then implement -both traits on a type `Human` that already has a method named `fly` implemented -on it. Each `fly` method does something different. - -- -```rust -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-17/src/main.rs:here}} -``` - - - -When we call `fly` on an instance of `Human`, the compiler defaults to calling -the method that is directly implemented on the type, as shown in Listing 20-18. - -- -```rust -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-18/src/main.rs:here}} -``` - - - -Running this code will print `*waving arms furiously*`, showing that Rust -called the `fly` method implemented on `Human` directly. - -To call the `fly` methods from either the `Pilot` trait or the `Wizard` trait, -we need to use more explicit syntax to specify which `fly` method we mean. -Listing 20-19 demonstrates this syntax. - -- -```rust -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-19/src/main.rs:here}} -``` - - - -Specifying the trait name before the method name clarifies to Rust which -implementation of `fly` we want to call. We could also write -`Human::fly(&person)`, which is equivalent to the `person.fly()` that we used -in Listing 20-19, but this is a bit longer to write if we don’t need to -disambiguate. - -Running this code prints the following: - -```console -{{#include ../listings/ch20-advanced-features/listing-20-19/output.txt}} -``` - -Because the `fly` method takes a `self` parameter, if we had two _types_ that -both implement one _trait_, Rust could figure out which implementation of a -trait to use based on the type of `self`. - -However, associated functions that are not methods don’t have a `self` -parameter. When there are multiple types or traits that define non-method -functions with the same function name, Rust doesn't always know which type you -mean unless you use _fully qualified syntax_. For example, in Listing 20-20 we -create a trait for an animal shelter that wants to name all baby dogs _Spot_. -We make an `Animal` trait with an associated non-method function `baby_name`. -The `Animal` trait is implemented for the struct `Dog`, on which we also -provide an associated non-method function `baby_name` directly. - -- -```rust -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-20/src/main.rs}} -``` - - - -We implement the code for naming all puppies Spot in the `baby_name` associated -function that is defined on `Dog`. The `Dog` type also implements the trait -`Animal`, which describes characteristics that all animals have. Baby dogs are -called puppies, and that is expressed in the implementation of the `Animal` -trait on `Dog` in the `baby_name` function associated with the `Animal` trait. - -In `main`, we call the `Dog::baby_name` function, which calls the associated -function defined on `Dog` directly. This code prints the following: - -```console -{{#include ../listings/ch20-advanced-features/listing-20-20/output.txt}} -``` - -This output isn’t what we wanted. We want to call the `baby_name` function that -is part of the `Animal` trait that we implemented on `Dog` so the code prints -`A baby dog is called a puppy`. The technique of specifying the trait name that -we used in Listing 20-19 doesn’t help here; if we change `main` to the code in -Listing 20-21, we’ll get a compilation error. - -- -```rust,ignore,does_not_compile -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-21/src/main.rs:here}} -``` - - - -Because `Animal::baby_name` doesn’t have a `self` parameter, and there could be -other types that implement the `Animal` trait, Rust can’t figure out which -implementation of `Animal::baby_name` we want. We’ll get this compiler error: - -```console -{{#include ../listings/ch20-advanced-features/listing-20-21/output.txt}} -``` - -To disambiguate and tell Rust that we want to use the implementation of -`Animal` for `Dog` as opposed to the implementation of `Animal` for some other -type, we need to use fully qualified syntax. Listing 20-22 demonstrates how to -use fully qualified syntax. - -- -```rust -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-22/src/main.rs:here}} -``` - - - -We’re providing Rust with a type annotation within the angle brackets, which -indicates we want to call the `baby_name` method from the `Animal` trait as -implemented on `Dog` by saying that we want to treat the `Dog` type as an -`Animal` for this function call. This code will now print what we want: - -```console -{{#include ../listings/ch20-advanced-features/listing-20-22/output.txt}} -``` - -In general, fully qualified syntax is defined as follows: - -```rust,ignore -::function(receiver_if_method, next_arg, ...); -``` - -For associated functions that aren’t methods, there would not be a `receiver`: -there would only be the list of other arguments. You could use fully qualified -syntax everywhere that you call functions or methods. However, you’re allowed -to omit any part of this syntax that Rust can figure out from other information -in the program. You only need to use this more verbose syntax in cases where -there are multiple implementations that use the same name and Rust needs help -to identify which implementation you want to call. - -### Using Supertraits to Require One Trait’s Functionality Within Another Trait - -Sometimes, you might write a trait definition that depends on another trait: -for a type to implement the first trait, you want to require that type to also -implement the second trait. You would do this so that your trait definition can -make use of the associated items of the second trait. The trait your trait -definition is relying on is called a _supertrait_ of your trait. - -For example, let’s say we want to make an `OutlinePrint` trait with an -`outline_print` method that will print a given value formatted so that it's -framed in asterisks. That is, given a `Point` struct that implements the -standard library trait `Display` to result in `(x, y)`, when we call -`outline_print` on a `Point` instance that has `1` for `x` and `3` for `y`, it -should print the following: - -```text -********** -* * -* (1, 3) * -* * -********** -``` - -In the implementation of the `outline_print` method, we want to use the -`Display` trait’s functionality. Therefore, we need to specify that the -`OutlinePrint` trait will work only for types that also implement `Display` and -provide the functionality that `OutlinePrint` needs. We can do that in the -trait definition by specifying `OutlinePrint: Display`. This technique is -similar to adding a trait bound to the trait. Listing 20-23 shows an -implementation of the `OutlinePrint` trait. - -- -```rust -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-23/src/main.rs:here}} -``` - - - -Because we’ve specified that `OutlinePrint` requires the `Display` trait, we -can use the `to_string` function that is automatically implemented for any type -that implements `Display`. If we tried to use `to_string` without adding a -colon and specifying the `Display` trait after the trait name, we’d get an -error saying that no method named `to_string` was found for the type `&Self` in -the current scope. - -Let’s see what happens when we try to implement `OutlinePrint` on a type that -doesn’t implement `Display`, such as the `Point` struct: - -- -```rust,ignore,does_not_compile -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-02-impl-outlineprint-for-point/src/main.rs:here}} -``` - - - -We get an error saying that `Display` is required but not implemented: - -```console -{{#include ../listings/ch20-advanced-features/no-listing-02-impl-outlineprint-for-point/output.txt}} -``` - -To fix this, we implement `Display` on `Point` and satisfy the constraint that -`OutlinePrint` requires, like so: - -- -```rust -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-03-impl-display-for-point/src/main.rs:here}} -``` - - - -Then implementing the `OutlinePrint` trait on `Point` will compile -successfully, and we can call `outline_print` on a `Point` instance to display -it within an outline of asterisks. - -### Using the Newtype Pattern to Implement External Traits on External Types - -In Chapter 10 in the [“Implementing a Trait on a -Type”][implementing-a-trait-on-a-type] section, we mentioned the -orphan rule that states we’re only allowed to implement a trait on a type if -either the trait or the type are local to our crate. It’s possible to get -around this restriction using the _newtype pattern_, which involves creating a -new type in a tuple struct. (We covered tuple structs in the [“Using Tuple -Structs without Named Fields to Create Different Types”][tuple-structs] section of Chapter 5.) The tuple struct will have one field and be a -thin wrapper around the type we want to implement a trait for. Then the wrapper -type is local to our crate, and we can implement the trait on the wrapper. -_Newtype_ is a term that originates from the Haskell programming language. -There is no runtime performance penalty for using this pattern, and the wrapper -type is elided at compile time. - -As an example, let’s say we want to implement `Display` on `Vec`, which the -orphan rule prevents us from doing directly because the `Display` trait and the -`Vec` type are defined outside our crate. We can make a `Wrapper` struct -that holds an instance of `Vec`; then we can implement `Display` on -`Wrapper` and use the `Vec` value, as shown in Listing 20-24. - -- -```rust -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-24/src/main.rs}} -``` - - - -The implementation of `Display` uses `self.0` to access the inner `Vec`, -because `Wrapper` is a tuple struct and `Vec` is the item at index 0 in the -tuple. Then we can use the functionality of the `Display` trait on `Wrapper`. - -The downside of using this technique is that `Wrapper` is a new type, so it -doesn’t have the methods of the value it’s holding. We would have to implement -all the methods of `Vec` directly on `Wrapper` such that the methods -delegate to `self.0`, which would allow us to treat `Wrapper` exactly like a -`Vec`. If we wanted the new type to have every method the inner type has, -implementing the `Deref` trait (discussed in Chapter 15 in the [“Treating Smart -Pointers Like Regular References with the `Deref` -Trait”][smart-pointer-deref] section) on the `Wrapper` to return -the inner type would be a solution. If we don’t want the `Wrapper` type to have -all the methods of the inner type—for example, to restrict the `Wrapper` type’s -behavior—we would have to implement just the methods we do want manually. - -This newtype pattern is also useful even when traits are not involved. Let’s -switch focus and look at some advanced ways to interact with Rust’s type system. - -[newtype]: ch20-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types -[implementing-a-trait-on-a-type]: ch10-02-traits.html#implementing-a-trait-on-a-type -[traits-defining-shared-behavior]: ch10-02-traits.html#traits-defining-shared-behavior -[smart-pointer-deref]: ch15-02-deref.html#treating-smart-pointers-like-regular-references-with-the-deref-trait -[tuple-structs]: ch05-01-defining-structs.html#using-tuple-structs-without-named-fields-to-create-different-types +# Advanced Traits diff --git a/src/ch20-03-advanced-types.md b/src/ch20-03-advanced-types.md new file mode 100644 index 0000000000..b61bdf6e2f --- /dev/null +++ b/src/ch20-03-advanced-types.md @@ -0,0 +1,298 @@ +## Advanced Types + +The Rust type system has some features that we’ve so far mentioned but haven’t +yet discussed. We’ll start by discussing newtypes in general as we examine why +newtypes are useful as types. Then we’ll move on to type aliases, a feature +similar to newtypes but with slightly different semantics. We’ll also discuss +the `!` type and dynamically sized types. + +### Using the Newtype Pattern for Type Safety and Abstraction + +> Note: This section assumes you’ve read the earlier section [“Using the +> Newtype Pattern to Implement External Traits on External +> Types.”][using-the-newtype-pattern] + +The newtype pattern is also useful for tasks beyond those we’ve discussed so +far, including statically enforcing that values are never confused and +indicating the units of a value. You saw an example of using newtypes to +indicate units in Listing 20-16: recall that the `Millimeters` and `Meters` +structs wrapped `u32` values in a newtype. If we wrote a function with a +parameter of type `Millimeters`, we couldn’t compile a program that +accidentally tried to call that function with a value of type `Meters` or a +plain `u32`. + +We can also use the newtype pattern to abstract away some implementation +details of a type: the new type can expose a public API that is different from +the API of the private inner type. + +Newtypes can also hide internal implementation. For example, we could provide a +`People` type to wrap a `HashMap` that stores a person’s ID +associated with their name. Code using `People` would only interact with the +public API we provide, such as a method to add a name string to the `People` +collection; that code wouldn’t need to know that we assign an `i32` ID to names +internally. The newtype pattern is a lightweight way to achieve encapsulation +to hide implementation details, which we discussed in the [“Encapsulation that +Hides Implementation +Details”][encapsulation-that-hides-implementation-details] +section of Chapter 18. + +### Creating Type Synonyms with Type Aliases + +Rust provides the ability to declare a _type alias_ to give an existing type +another name. For this we use the `type` keyword. For example, we can create +the alias `Kilometers` to `i32` like so: + +```rust +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-04-kilometers-alias/src/main.rs:here}} +``` + +Now, the alias `Kilometers` is a _synonym_ for `i32`; unlike the `Millimeters` +and `Meters` types we created in Listing 20-16, `Kilometers` is not a separate, +new type. Values that have the type `Kilometers` will be treated the same as +values of type `i32`: + +```rust +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-04-kilometers-alias/src/main.rs:there}} +``` + +Because `Kilometers` and `i32` are the same type, we can add values of both +types and we can pass `Kilometers` values to functions that take `i32` +parameters. However, using this method, we don’t get the type checking benefits +that we get from the newtype pattern discussed earlier. In other words, if we +mix up `Kilometers` and `i32` values somewhere, the compiler will not give us +an error. + +The main use case for type synonyms is to reduce repetition. For example, we +might have a lengthy type like this: + +```rust,ignore +Box +``` + +Writing this lengthy type in function signatures and as type annotations all +over the code can be tiresome and error prone. Imagine having a project full of +code like that in Listing 20-25. + ++ +```rust +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-25/src/main.rs:here}} +``` + + + +A type alias makes this code more manageable by reducing the repetition. In +Listing 20-26, we’ve introduced an alias named `Thunk` for the verbose type and +can replace all uses of the type with the shorter alias `Thunk`. + ++ +```rust +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-26/src/main.rs:here}} +``` + + + +This code is much easier to read and write! Choosing a meaningful name for a +type alias can help communicate your intent as well (_thunk_ is a word for code +to be evaluated at a later time, so it’s an appropriate name for a closure that +gets stored). + +Type aliases are also commonly used with the `Result` type for reducing +repetition. Consider the `std::io` module in the standard library. I/O +operations often return a `Result` to handle situations when operations +fail to work. This library has a `std::io::Error` struct that represents all +possible I/O errors. Many of the functions in `std::io` will be returning +`Result` where the `E` is `std::io::Error`, such as these functions in +the `Write` trait: + +```rust,noplayground +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-05-write-trait/src/lib.rs}} +``` + +The `Result<..., Error>` is repeated a lot. As such, `std::io` has this type +alias declaration: + +```rust,noplayground +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-06-result-alias/src/lib.rs:here}} +``` + +Because this declaration is in the `std::io` module, we can use the fully +qualified alias `std::io::Result`; that is, a `Result` with the `E` +filled in as `std::io::Error`. The `Write` trait function signatures end up +looking like this: + +```rust,noplayground +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-06-result-alias/src/lib.rs:there}} +``` + +The type alias helps in two ways: it makes code easier to write _and_ it gives +us a consistent interface across all of `std::io`. Because it’s an alias, it’s +just another `Result`, which means we can use any methods that work on +`Result` with it, as well as special syntax like the `?` operator. + +### The Never Type that Never Returns + +Rust has a special type named `!` that’s known in type theory lingo as the +_empty type_ because it has no values. We prefer to call it the _never type_ +because it stands in the place of the return type when a function will never +return. Here is an example: + +```rust,noplayground +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-07-never-type/src/lib.rs:here}} +``` + +This code is read as “the function `bar` returns never.” Functions that return +never are called _diverging functions_. We can’t create values of the type `!` +so `bar` can never possibly return. + +But what use is a type you can never create values for? Recall the code from +Listing 2-5, part of the number guessing game; we’ve reproduced a bit of it +here in Listing 20-27. + ++ +```rust,ignore +{{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-05/src/main.rs:ch19}} +``` + + + +At the time, we skipped over some details in this code. In Chapter 6 in [“The +`match` Control Flow Operator”][the-match-control-flow-operator] +section, we discussed that `match` arms must all return the same type. So, for +example, the following code doesn’t work: + +```rust,ignore,does_not_compile +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-08-match-arms-different-types/src/main.rs:here}} +``` + +The type of `guess` in this code would have to be an integer _and_ a string, +and Rust requires that `guess` have only one type. So what does `continue` +return? How were we allowed to return a `u32` from one arm and have another arm +that ends with `continue` in Listing 20-27? + +As you might have guessed, `continue` has a `!` value. That is, when Rust +computes the type of `guess`, it looks at both match arms, the former with a +value of `u32` and the latter with a `!` value. Because `!` can never have a +value, Rust decides that the type of `guess` is `u32`. + +The formal way of describing this behavior is that expressions of type `!` can +be coerced into any other type. We’re allowed to end this `match` arm with +`continue` because `continue` doesn’t return a value; instead, it moves control +back to the top of the loop, so in the `Err` case, we never assign a value to +`guess`. + +The never type is useful with the `panic!` macro as well. Recall the `unwrap` +function that we call on `Option` values to produce a value or panic with +this definition: + +```rust,ignore +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-09-unwrap-definition/src/lib.rs:here}} +``` + +In this code, the same thing happens as in the `match` in Listing 20-27: Rust +sees that `val` has the type `T` and `panic!` has the type `!`, so the result +of the overall `match` expression is `T`. This code works because `panic!` +doesn’t produce a value; it ends the program. In the `None` case, we won’t be +returning a value from `unwrap`, so this code is valid. + +One final expression that has the type `!` is a `loop`: + +```rust,ignore +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-10-loop-returns-never/src/main.rs:here}} +``` + +Here, the loop never ends, so `!` is the value of the expression. However, this +wouldn’t be true if we included a `break`, because the loop would terminate +when it got to the `break`. + +### Dynamically Sized Types and the `Sized` Trait + +Rust needs to know certain details about its types, such as how much space to +allocate for a value of a particular type. This leaves one corner of its type +system a little confusing at first: the concept of _dynamically sized types_. +Sometimes referred to as _DSTs_ or _unsized types_, these types let us write +code using values whose size we can know only at runtime. + +Let’s dig into the details of a dynamically sized type called `str`, which +we’ve been using throughout the book. That’s right, not `&str`, but `str` on +its own, is a DST. We can’t know how long the string is until runtime, meaning +we can’t create a variable of type `str`, nor can we take an argument of type +`str`. Consider the following code, which does not work: + +```rust,ignore,does_not_compile +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-11-cant-create-str/src/main.rs:here}} +``` + +Rust needs to know how much memory to allocate for any value of a particular +type, and all values of a type must use the same amount of memory. If Rust +allowed us to write this code, these two `str` values would need to take up the +same amount of space. But they have different lengths: `s1` needs 12 bytes of +storage and `s2` needs 15. This is why it’s not possible to create a variable +holding a dynamically sized type. + +So what do we do? In this case, you already know the answer: we make the types +of `s1` and `s2` a `&str` rather than a `str`. Recall from the [“String +Slices”][string-slices] section of Chapter 4 that the slice data +structure just stores the starting position and the length of the slice. So +although a `&T` is a single value that stores the memory address of where the +`T` is located, a `&str` is _two_ values: the address of the `str` and its +length. As such, we can know the size of a `&str` value at compile time: it’s +twice the length of a `usize`. That is, we always know the size of a `&str`, no +matter how long the string it refers to is. In general, this is the way in +which dynamically sized types are used in Rust: they have an extra bit of +metadata that stores the size of the dynamic information. The golden rule of +dynamically sized types is that we must always put values of dynamically sized +types behind a pointer of some kind. + +We can combine `str` with all kinds of pointers: for example, `Box` or +`Rc`. In fact, you’ve seen this before but with a different dynamically +sized type: traits. Every trait is a dynamically sized type we can refer to by +using the name of the trait. In Chapter 18 in the [“Using Trait Objects That +Allow for Values of Different +Types”][using-trait-objects-that-allow-for-values-of-different-types] section, we mentioned that to use traits as trait objects, we must +put them behind a pointer, such as `&dyn Trait` or `Box` (`Rc` would work too). + +To work with DSTs, Rust provides the `Sized` trait to determine whether or not +a type’s size is known at compile time. This trait is automatically implemented +for everything whose size is known at compile time. In addition, Rust +implicitly adds a bound on `Sized` to every generic function. That is, a +generic function definition like this: + +```rust,ignore +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-12-generic-fn-definition/src/lib.rs}} +``` + +is actually treated as though we had written this: + +```rust,ignore +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-13-generic-implicit-sized-bound/src/lib.rs}} +``` + +By default, generic functions will work only on types that have a known size at +compile time. However, you can use the following special syntax to relax this +restriction: + +```rust,ignore +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-14-generic-maybe-sized/src/lib.rs}} +``` + +A trait bound on `?Sized` means “`T` may or may not be `Sized`” and this +notation overrides the default that generic types must have a known size at +compile time. The `?Trait` syntax with this meaning is only available for +`Sized`, not any other traits. + +Also note that we switched the type of the `t` parameter from `T` to `&T`. +Because the type might not be `Sized`, we need to use it behind some kind of +pointer. In this case, we’ve chosen a reference. + +Next, we’ll talk about functions and closures! + +[encapsulation-that-hides-implementation-details]: ch18-01-what-is-oo.html#encapsulation-that-hides-implementation-details +[string-slices]: ch04-03-slices.html#string-slices +[the-match-control-flow-operator]: ch06-02-match.html#the-match-control-flow-operator +[using-trait-objects-that-allow-for-values-of-different-types]: ch18-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types +[using-the-newtype-pattern]: ch20-02-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types diff --git a/src/ch20-04-advanced-functions-and-closures.md b/src/ch20-04-advanced-functions-and-closures.md new file mode 100644 index 0000000000..e8692eea93 --- /dev/null +++ b/src/ch20-04-advanced-functions-and-closures.md @@ -0,0 +1,127 @@ +## Advanced Functions and Closures + +This section explores some advanced features related to functions and closures, +including function pointers and returning closures. + +### Function Pointers + +We’ve talked about how to pass closures to functions; you can also pass regular +functions to functions! This technique is useful when you want to pass a +function you’ve already defined rather than defining a new closure. Functions +coerce to the type `fn` (with a lowercase f), not to be confused with the `Fn` +closure trait. The `fn` type is called a _function pointer_. Passing functions +with function pointers will allow you to use functions as arguments to other +functions. + +The syntax for specifying that a parameter is a function pointer is similar to +that of closures, as shown in Listing 20-28, where we’ve defined a function +`add_one` that adds one to its parameter. The function `do_twice` takes two +parameters: a function pointer to any function that takes an `i32` parameter +and returns an `i32`, and one `i32` value. The `do_twice` function calls the +function `f` twice, passing it the `arg` value, then adds the two function call +results together. The `main` function calls `do_twice` with the arguments +`add_one` and `5`. + ++ +```rust +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-28/src/main.rs}} +``` + + + +This code prints `The answer is: 12`. We specify that the parameter `f` in +`do_twice` is an `fn` that takes one parameter of type `i32` and returns an +`i32`. We can then call `f` in the body of `do_twice`. In `main`, we can pass +the function name `add_one` as the first argument to `do_twice`. + +Unlike closures, `fn` is a type rather than a trait, so we specify `fn` as the +parameter type directly rather than declaring a generic type parameter with one +of the `Fn` traits as a trait bound. + +Function pointers implement all three of the closure traits (`Fn`, `FnMut`, and +`FnOnce`), meaning you can always pass a function pointer as an argument for a +function that expects a closure. It’s best to write functions using a generic +type and one of the closure traits so your functions can accept either +functions or closures. + +That said, one example of where you would want to only accept `fn` and not +closures is when interfacing with external code that doesn’t have closures: C +functions can accept functions as arguments, but C doesn’t have closures. + +As an example of where you could use either a closure defined inline or a named +function, let’s look at a use of the `map` method provided by the `Iterator` +trait in the standard library. To use the `map` function to turn a vector of +numbers into a vector of strings, we could use a closure, like this: + +```rust +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-15-map-closure/src/main.rs:here}} +``` + +Or we could name a function as the argument to `map` instead of the closure, +like this: + +```rust +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-16-map-function/src/main.rs:here}} +``` + +Note that we must use the fully qualified syntax that we talked about earlier +in the [“Advanced Traits”][advanced-traits] section because +there are multiple functions available named `to_string`. Here, we’re using the +`to_string` function defined in the `ToString` trait, which the standard +library has implemented for any type that implements `Display`. + +Recall from the [“Enum values”][enum-values] section of Chapter +6 that the name of each enum variant that we define also becomes an initializer +function. We can use these initializer functions as function pointers that +implement the closure traits, which means we can specify the initializer +functions as arguments for methods that take closures, like so: + +```rust +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-17-map-initializer/src/main.rs:here}} +``` + +Here we create `Status::Value` instances using each `u32` value in the range +that `map` is called on by using the initializer function of `Status::Value`. +Some people prefer this style, and some people prefer to use closures. They +compile to the same code, so use whichever style is clearer to you. + +### Returning Closures + +Closures are represented by traits, which means you can’t return closures +directly. In most cases where you might want to return a trait, you can instead +use the concrete type that implements the trait as the return value of the +function. However, you can’t do that with closures because they don’t have a +concrete type that is returnable; you’re not allowed to use the function +pointer `fn` as a return type, for example. + +Instead, you will normally use the `impl Trait` syntax we learned about in +Chapter 10. You can return any function type, using `Fn`, `FnOnce` and `FnMut`. +For example, this code will work just fine: + +```rust,ignore,does_not_compile +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-18-returns-closure/src/lib.rs}} +``` + +However, as we noted in the [“Closure Type Inference and +Annotation”][closure-types] section in Chapter 13, each closure +is also its own distinct type. If you need to work with multiple functions that +have the same signature but different implementations, you will need to use a +trait object for them: + +```rust,noplayground +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-19-returns-closure-trait-object/src/main.rs}} +``` + +This code will compile just fine—but it wouldn’t if we had tried to stick with +`impl Fn(i32) -> i32`. For more about trait objects, refer to the section +[“Using Trait Objects That Allow for Values of Different +Types”][using-trait-objects-that-allow-for-values-of-different-types] in Chapter 18. + +Next, let’s look at macros! + +[advanced-traits]: ch20-02-advanced-traits.html#advanced-traits +[enum-values]: ch06-01-defining-an-enum.html#enum-values +[closure-types]: ch13-01-closures.html#closure-type-inference-and-annotation +[using-trait-objects-that-allow-for-values-of-different-types]: ch18-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types diff --git a/src/ch20-04-advanced-types.md b/src/ch20-04-advanced-types.md index 498a3c36b6..dccf4da0af 100644 --- a/src/ch20-04-advanced-types.md +++ b/src/ch20-04-advanced-types.md @@ -1,298 +1 @@ -## Advanced Types - -The Rust type system has some features that we’ve so far mentioned but haven’t -yet discussed. We’ll start by discussing newtypes in general as we examine why -newtypes are useful as types. Then we’ll move on to type aliases, a feature -similar to newtypes but with slightly different semantics. We’ll also discuss -the `!` type and dynamically sized types. - -### Using the Newtype Pattern for Type Safety and Abstraction - -> Note: This section assumes you’ve read the earlier section [“Using the -> Newtype Pattern to Implement External Traits on External -> Types.”][using-the-newtype-pattern] - -The newtype pattern is also useful for tasks beyond those we’ve discussed so -far, including statically enforcing that values are never confused and -indicating the units of a value. You saw an example of using newtypes to -indicate units in Listing 20-16: recall that the `Millimeters` and `Meters` -structs wrapped `u32` values in a newtype. If we wrote a function with a -parameter of type `Millimeters`, we couldn’t compile a program that -accidentally tried to call that function with a value of type `Meters` or a -plain `u32`. - -We can also use the newtype pattern to abstract away some implementation -details of a type: the new type can expose a public API that is different from -the API of the private inner type. - -Newtypes can also hide internal implementation. For example, we could provide a -`People` type to wrap a `HashMap` that stores a person’s ID -associated with their name. Code using `People` would only interact with the -public API we provide, such as a method to add a name string to the `People` -collection; that code wouldn’t need to know that we assign an `i32` ID to names -internally. The newtype pattern is a lightweight way to achieve encapsulation -to hide implementation details, which we discussed in the [“Encapsulation that -Hides Implementation -Details”][encapsulation-that-hides-implementation-details] -section of Chapter 18. - -### Creating Type Synonyms with Type Aliases - -Rust provides the ability to declare a _type alias_ to give an existing type -another name. For this we use the `type` keyword. For example, we can create -the alias `Kilometers` to `i32` like so: - -```rust -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-04-kilometers-alias/src/main.rs:here}} -``` - -Now, the alias `Kilometers` is a _synonym_ for `i32`; unlike the `Millimeters` -and `Meters` types we created in Listing 20-16, `Kilometers` is not a separate, -new type. Values that have the type `Kilometers` will be treated the same as -values of type `i32`: - -```rust -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-04-kilometers-alias/src/main.rs:there}} -``` - -Because `Kilometers` and `i32` are the same type, we can add values of both -types and we can pass `Kilometers` values to functions that take `i32` -parameters. However, using this method, we don’t get the type checking benefits -that we get from the newtype pattern discussed earlier. In other words, if we -mix up `Kilometers` and `i32` values somewhere, the compiler will not give us -an error. - -The main use case for type synonyms is to reduce repetition. For example, we -might have a lengthy type like this: - -```rust,ignore -Box -``` - -Writing this lengthy type in function signatures and as type annotations all -over the code can be tiresome and error prone. Imagine having a project full of -code like that in Listing 20-25. - -- -```rust -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-25/src/main.rs:here}} -``` - - - -A type alias makes this code more manageable by reducing the repetition. In -Listing 20-26, we’ve introduced an alias named `Thunk` for the verbose type and -can replace all uses of the type with the shorter alias `Thunk`. - -- -```rust -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-26/src/main.rs:here}} -``` - - - -This code is much easier to read and write! Choosing a meaningful name for a -type alias can help communicate your intent as well (_thunk_ is a word for code -to be evaluated at a later time, so it’s an appropriate name for a closure that -gets stored). - -Type aliases are also commonly used with the `Result` type for reducing -repetition. Consider the `std::io` module in the standard library. I/O -operations often return a `Result` to handle situations when operations -fail to work. This library has a `std::io::Error` struct that represents all -possible I/O errors. Many of the functions in `std::io` will be returning -`Result` where the `E` is `std::io::Error`, such as these functions in -the `Write` trait: - -```rust,noplayground -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-05-write-trait/src/lib.rs}} -``` - -The `Result<..., Error>` is repeated a lot. As such, `std::io` has this type -alias declaration: - -```rust,noplayground -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-06-result-alias/src/lib.rs:here}} -``` - -Because this declaration is in the `std::io` module, we can use the fully -qualified alias `std::io::Result`; that is, a `Result` with the `E` -filled in as `std::io::Error`. The `Write` trait function signatures end up -looking like this: - -```rust,noplayground -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-06-result-alias/src/lib.rs:there}} -``` - -The type alias helps in two ways: it makes code easier to write _and_ it gives -us a consistent interface across all of `std::io`. Because it’s an alias, it’s -just another `Result`, which means we can use any methods that work on -`Result` with it, as well as special syntax like the `?` operator. - -### The Never Type that Never Returns - -Rust has a special type named `!` that’s known in type theory lingo as the -_empty type_ because it has no values. We prefer to call it the _never type_ -because it stands in the place of the return type when a function will never -return. Here is an example: - -```rust,noplayground -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-07-never-type/src/lib.rs:here}} -``` - -This code is read as “the function `bar` returns never.” Functions that return -never are called _diverging functions_. We can’t create values of the type `!` -so `bar` can never possibly return. - -But what use is a type you can never create values for? Recall the code from -Listing 2-5, part of the number guessing game; we’ve reproduced a bit of it -here in Listing 20-27. - -- -```rust,ignore -{{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-05/src/main.rs:ch19}} -``` - - - -At the time, we skipped over some details in this code. In Chapter 6 in [“The -`match` Control Flow Operator”][the-match-control-flow-operator] -section, we discussed that `match` arms must all return the same type. So, for -example, the following code doesn’t work: - -```rust,ignore,does_not_compile -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-08-match-arms-different-types/src/main.rs:here}} -``` - -The type of `guess` in this code would have to be an integer _and_ a string, -and Rust requires that `guess` have only one type. So what does `continue` -return? How were we allowed to return a `u32` from one arm and have another arm -that ends with `continue` in Listing 20-27? - -As you might have guessed, `continue` has a `!` value. That is, when Rust -computes the type of `guess`, it looks at both match arms, the former with a -value of `u32` and the latter with a `!` value. Because `!` can never have a -value, Rust decides that the type of `guess` is `u32`. - -The formal way of describing this behavior is that expressions of type `!` can -be coerced into any other type. We’re allowed to end this `match` arm with -`continue` because `continue` doesn’t return a value; instead, it moves control -back to the top of the loop, so in the `Err` case, we never assign a value to -`guess`. - -The never type is useful with the `panic!` macro as well. Recall the `unwrap` -function that we call on `Option` values to produce a value or panic with -this definition: - -```rust,ignore -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-09-unwrap-definition/src/lib.rs:here}} -``` - -In this code, the same thing happens as in the `match` in Listing 20-27: Rust -sees that `val` has the type `T` and `panic!` has the type `!`, so the result -of the overall `match` expression is `T`. This code works because `panic!` -doesn’t produce a value; it ends the program. In the `None` case, we won’t be -returning a value from `unwrap`, so this code is valid. - -One final expression that has the type `!` is a `loop`: - -```rust,ignore -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-10-loop-returns-never/src/main.rs:here}} -``` - -Here, the loop never ends, so `!` is the value of the expression. However, this -wouldn’t be true if we included a `break`, because the loop would terminate -when it got to the `break`. - -### Dynamically Sized Types and the `Sized` Trait - -Rust needs to know certain details about its types, such as how much space to -allocate for a value of a particular type. This leaves one corner of its type -system a little confusing at first: the concept of _dynamically sized types_. -Sometimes referred to as _DSTs_ or _unsized types_, these types let us write -code using values whose size we can know only at runtime. - -Let’s dig into the details of a dynamically sized type called `str`, which -we’ve been using throughout the book. That’s right, not `&str`, but `str` on -its own, is a DST. We can’t know how long the string is until runtime, meaning -we can’t create a variable of type `str`, nor can we take an argument of type -`str`. Consider the following code, which does not work: - -```rust,ignore,does_not_compile -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-11-cant-create-str/src/main.rs:here}} -``` - -Rust needs to know how much memory to allocate for any value of a particular -type, and all values of a type must use the same amount of memory. If Rust -allowed us to write this code, these two `str` values would need to take up the -same amount of space. But they have different lengths: `s1` needs 12 bytes of -storage and `s2` needs 15. This is why it’s not possible to create a variable -holding a dynamically sized type. - -So what do we do? In this case, you already know the answer: we make the types -of `s1` and `s2` a `&str` rather than a `str`. Recall from the [“String -Slices”][string-slices] section of Chapter 4 that the slice data -structure just stores the starting position and the length of the slice. So -although a `&T` is a single value that stores the memory address of where the -`T` is located, a `&str` is _two_ values: the address of the `str` and its -length. As such, we can know the size of a `&str` value at compile time: it’s -twice the length of a `usize`. That is, we always know the size of a `&str`, no -matter how long the string it refers to is. In general, this is the way in -which dynamically sized types are used in Rust: they have an extra bit of -metadata that stores the size of the dynamic information. The golden rule of -dynamically sized types is that we must always put values of dynamically sized -types behind a pointer of some kind. - -We can combine `str` with all kinds of pointers: for example, `Box` or -`Rc`. In fact, you’ve seen this before but with a different dynamically -sized type: traits. Every trait is a dynamically sized type we can refer to by -using the name of the trait. In Chapter 18 in the [“Using Trait Objects That -Allow for Values of Different -Types”][using-trait-objects-that-allow-for-values-of-different-types] section, we mentioned that to use traits as trait objects, we must -put them behind a pointer, such as `&dyn Trait` or `Box` (`Rc` would work too). - -To work with DSTs, Rust provides the `Sized` trait to determine whether or not -a type’s size is known at compile time. This trait is automatically implemented -for everything whose size is known at compile time. In addition, Rust -implicitly adds a bound on `Sized` to every generic function. That is, a -generic function definition like this: - -```rust,ignore -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-12-generic-fn-definition/src/lib.rs}} -``` - -is actually treated as though we had written this: - -```rust,ignore -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-13-generic-implicit-sized-bound/src/lib.rs}} -``` - -By default, generic functions will work only on types that have a known size at -compile time. However, you can use the following special syntax to relax this -restriction: - -```rust,ignore -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-14-generic-maybe-sized/src/lib.rs}} -``` - -A trait bound on `?Sized` means “`T` may or may not be `Sized`” and this -notation overrides the default that generic types must have a known size at -compile time. The `?Trait` syntax with this meaning is only available for -`Sized`, not any other traits. - -Also note that we switched the type of the `t` parameter from `T` to `&T`. -Because the type might not be `Sized`, we need to use it behind some kind of -pointer. In this case, we’ve chosen a reference. - -Next, we’ll talk about functions and closures! - -[encapsulation-that-hides-implementation-details]: ch18-01-what-is-oo.html#encapsulation-that-hides-implementation-details -[string-slices]: ch04-03-slices.html#string-slices -[the-match-control-flow-operator]: ch06-02-match.html#the-match-control-flow-operator -[using-trait-objects-that-allow-for-values-of-different-types]: ch18-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types -[using-the-newtype-pattern]: ch20-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types +# Advanced Types diff --git a/src/ch20-05-advanced-functions-and-closures.md b/src/ch20-05-advanced-functions-and-closures.md index 637c31b559..84515557a7 100644 --- a/src/ch20-05-advanced-functions-and-closures.md +++ b/src/ch20-05-advanced-functions-and-closures.md @@ -1,127 +1 @@ -## Advanced Functions and Closures - -This section explores some advanced features related to functions and closures, -including function pointers and returning closures. - -### Function Pointers - -We’ve talked about how to pass closures to functions; you can also pass regular -functions to functions! This technique is useful when you want to pass a -function you’ve already defined rather than defining a new closure. Functions -coerce to the type `fn` (with a lowercase f), not to be confused with the `Fn` -closure trait. The `fn` type is called a _function pointer_. Passing functions -with function pointers will allow you to use functions as arguments to other -functions. - -The syntax for specifying that a parameter is a function pointer is similar to -that of closures, as shown in Listing 20-28, where we’ve defined a function -`add_one` that adds one to its parameter. The function `do_twice` takes two -parameters: a function pointer to any function that takes an `i32` parameter -and returns an `i32`, and one `i32` value. The `do_twice` function calls the -function `f` twice, passing it the `arg` value, then adds the two function call -results together. The `main` function calls `do_twice` with the arguments -`add_one` and `5`. - -- -```rust -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-28/src/main.rs}} -``` - - - -This code prints `The answer is: 12`. We specify that the parameter `f` in -`do_twice` is an `fn` that takes one parameter of type `i32` and returns an -`i32`. We can then call `f` in the body of `do_twice`. In `main`, we can pass -the function name `add_one` as the first argument to `do_twice`. - -Unlike closures, `fn` is a type rather than a trait, so we specify `fn` as the -parameter type directly rather than declaring a generic type parameter with one -of the `Fn` traits as a trait bound. - -Function pointers implement all three of the closure traits (`Fn`, `FnMut`, and -`FnOnce`), meaning you can always pass a function pointer as an argument for a -function that expects a closure. It’s best to write functions using a generic -type and one of the closure traits so your functions can accept either -functions or closures. - -That said, one example of where you would want to only accept `fn` and not -closures is when interfacing with external code that doesn’t have closures: C -functions can accept functions as arguments, but C doesn’t have closures. - -As an example of where you could use either a closure defined inline or a named -function, let’s look at a use of the `map` method provided by the `Iterator` -trait in the standard library. To use the `map` function to turn a vector of -numbers into a vector of strings, we could use a closure, like this: - -```rust -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-15-map-closure/src/main.rs:here}} -``` - -Or we could name a function as the argument to `map` instead of the closure, -like this: - -```rust -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-16-map-function/src/main.rs:here}} -``` - -Note that we must use the fully qualified syntax that we talked about earlier -in the [“Advanced Traits”][advanced-traits] section because -there are multiple functions available named `to_string`. Here, we’re using the -`to_string` function defined in the `ToString` trait, which the standard -library has implemented for any type that implements `Display`. - -Recall from the [“Enum values”][enum-values] section of Chapter -6 that the name of each enum variant that we define also becomes an initializer -function. We can use these initializer functions as function pointers that -implement the closure traits, which means we can specify the initializer -functions as arguments for methods that take closures, like so: - -```rust -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-17-map-initializer/src/main.rs:here}} -``` - -Here we create `Status::Value` instances using each `u32` value in the range -that `map` is called on by using the initializer function of `Status::Value`. -Some people prefer this style, and some people prefer to use closures. They -compile to the same code, so use whichever style is clearer to you. - -### Returning Closures - -Closures are represented by traits, which means you can’t return closures -directly. In most cases where you might want to return a trait, you can instead -use the concrete type that implements the trait as the return value of the -function. However, you can’t do that with closures because they don’t have a -concrete type that is returnable; you’re not allowed to use the function -pointer `fn` as a return type, for example. - -Instead, you will normally use the `impl Trait` syntax we learned about in -Chapter 10. You can return any function type, using `Fn`, `FnOnce` and `FnMut`. -For example, this code will work just fine: - -```rust,ignore,does_not_compile -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-18-returns-closure/src/lib.rs}} -``` - -However, as we noted in the [“Closure Type Inference and -Annotation”][closure-types] section in Chapter 13, each closure -is also its own distinct type. If you need to work with multiple functions that -have the same signature but different implementations, you will need to use a -trait object for them: - -```rust,noplayground -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-19-returns-closure-trait-object/src/main.rs}} -``` - -This code will compile just fine—but it wouldn’t if we had tried to stick with -`impl Fn(i32) -> i32`. For more about trait objects, refer to the section -[“Using Trait Objects That Allow for Values of Different -Types”][using-trait-objects-that-allow-for-values-of-different-types] in Chapter 18. - -Next, let’s look at macros! - -[advanced-traits]: ch20-03-advanced-traits.html#advanced-traits -[enum-values]: ch06-01-defining-an-enum.html#enum-values -[closure-types]: ch13-01-closures.html#closure-type-inference-and-annotation -[using-trait-objects-that-allow-for-values-of-different-types]: ch18-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types +# Advanced Functions and Closures diff --git a/src/ch20-05-macros.md b/src/ch20-05-macros.md new file mode 100644 index 0000000000..edc5fc4c8f --- /dev/null +++ b/src/ch20-05-macros.md @@ -0,0 +1,514 @@ +## Macros + +We’ve used macros like `println!` throughout this book, but we haven’t fully +explored what a macro is and how it works. The term _macro_ refers to a family +of features in Rust: _declarative_ macros with `macro_rules!` and three kinds +of _procedural_ macros: + +- Custom `#[derive]` macros that specify code added with the `derive` attribute + used on structs and enums +- Attribute-like macros that define custom attributes usable on any item +- Function-like macros that look like function calls but operate on the tokens + specified as their argument + +We’ll talk about each of these in turn, but first, let’s look at why we even +need macros when we already have 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 Appendix C, we discuss the `derive` +attribute, which generates an implementation of various traits for you. We’ve +also used the `println!` and `vec!` macros throughout the book. All of these +macros _expand_ to produce more code than the code you’ve written manually. + +Metaprogramming is useful for reducing the amount of code you have to write and +maintain, which is also one of the roles of functions. However, macros have +some additional powers that functions don’t. + +A function signature must declare the number and type of parameters the +function has. Macros, on the other hand, can take a variable number of +parameters: we can call `println!("hello")` with one argument or +`println!("hello {}", name)` with two arguments. Also, macros are expanded +before the compiler interprets the meaning of the code, so a macro can, for +example, implement a trait on a given type. A function can’t, because it gets +called at runtime and a trait needs to be implemented at compile time. + +The downside to implementing a macro instead of a function is that macro +definitions are more complex than function definitions because you’re 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 important difference between macros and functions is that you must +define macros or bring them into scope _before_ you call them in a file, as +opposed to functions you can define anywhere and call anywhere. + +### Declarative Macros with `macro_rules!` for General Metaprogramming + +The most widely used form of macros in Rust is the _declarative macro_. 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 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. + +To define a macro, you use the `macro_rules!` construct. Let’s explore how to +use `macro_rules!` by looking at how the `vec!` macro is defined. Chapter 8 +covered how we can use the `vec!` macro to create a new vector with particular +values. For example, the following macro creates a new vector containing three +integers: + +```rust +let v: Vec = vec![1, 2, 3]; +``` + +We could also use the `vec!` macro to make a vector of two integers or a vector +of five string slices. We wouldn’t be able to use a function to do the same +because we wouldn’t know the number or type of values up front. + +Listing 20-29 shows a slightly simplified definition of the `vec!` macro. + ++ +```rust,noplayground +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-29/src/lib.rs}} +``` + + + +> Note: The actual definition of the `vec!` macro in the standard library +> includes code to preallocate the correct amount of memory up front. That code +> is an optimization that we don’t include here to make the example simpler. + +The `#[macro_export]` 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 can’t be brought into scope. + +We then start the macro definition with `macro_rules!` and the name of the +macro we’re defining _without_ the exclamation mark. The name, in this case +`vec`, is followed by curly brackets denoting the body of the macro definition. + +The structure in the `vec!` body is similar to the structure of a `match` +expression. Here we have one arm with the pattern `( $( $x:expr ),* )`, +followed by `=>` and the block of code associated with this pattern. If the +pattern matches, the associated block of code will be emitted. Given that this +is the only pattern in this macro, there is only one valid way to match; any +other pattern will result in an error. More complex macros will have more than +one arm. + +Valid pattern syntax in macro definitions is different than the pattern syntax +covered in Chapter 19 because macro patterns are matched against Rust code +structure rather than values. Let’s walk through what the pattern pieces in +Listing 20-29 mean; for the full macro pattern syntax, see the [Rust +Reference][ref]. + +First, we use a set of parentheses to encompass the whole pattern. We use a +dollar sign (`$`) to declare a variable in the macro system that will contain +the Rust code matching the pattern. The dollar sign makes it clear this is a +macro variable as opposed to a regular Rust variable. Next comes a set of +parentheses that captures values that match the pattern within the parentheses +for use in the replacement code. Within `$()` is `$x:expr`, which matches any +Rust expression and gives the expression the name `$x`. + +The comma following `$()` indicates that a literal comma separator character +must appear between each instance of the code that matches the code within +`$()`. The `*` 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: +`temp_vec.push()` within `$()*` is generated for each part that matches `$()` +in the pattern zero or more times depending on how many times the pattern +matches. The `$x` is replaced with each expression matched. When we call this +macro with `vec![1, 2, 3];`, the code generated that replaces this macro call +will be the following: + +```rust,ignore +{ + let mut temp_vec = Vec::new(); + temp_vec.push(1); + temp_vec.push(2); + temp_vec.push(3); + temp_vec +} +``` + +We’ve 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. + +To learn more about how to write macros, consult the online documentation or +other resources, such as [“The Little Book of Rust Macros”][tlborm] started by +Daniel Keep and continued by Lukas Wirth. + +### Procedural Macros for Generating Code from Attributes + +The second form of macros is the _procedural macro_, which acts more like a +function (and is 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. The three kinds of procedural macros are custom derive, +attribute-like, and function-like, and all work in a similar fashion. + +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. In Listing 20-30, we show how to define a +procedural macro, where `some_attribute` is a placeholder for using a specific +macro variety. + ++ +```rust,ignore +use proc_macro; + +#[some_attribute] +pub fn some_name(input: TokenStream) -> TokenStream { +} +``` + + + +The function that defines a procedural macro takes a `TokenStream` as an input +and produces a `TokenStream` as an output. The `TokenStream` type is defined by +the `proc_macro` 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 `TokenStream`, and the code the macro produces +is the output `TokenStream`. The function also has an attribute attached to it +that specifies which kind of procedural macro we’re creating. We can have +multiple kinds of procedural macros in the same crate. + +Let’s look at the different kinds of procedural macros. We’ll start with a +custom derive macro and then explain the small dissimilarities that make the +other forms different. + +### How to Write a Custom `derive` Macro + +Let’s create a crate named `hello_macro` that defines a trait named +`HelloMacro` with one associated function named `hello_macro`. Rather than +making our users implement the `HelloMacro` trait for each of their types, +we’ll provide a procedural macro so users can annotate their type with +`#[derive(HelloMacro)]` to get a default implementation of the `hello_macro` +function. The default implementation will print `Hello, Macro! My name is +TypeName!` where `TypeName` is the name of the type on which this trait has +been defined. In other words, we’ll write a crate that enables another +programmer to write code like Listing 20-31 using our crate. + ++ +```rust,ignore,does_not_compile +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-31/src/main.rs}} +``` + + + +This code will print `Hello, Macro! My name is Pancakes!` when we’re done. The +first step is to make a new library crate, like this: + +```console +$ cargo new hello_macro --lib +``` + +Next, we’ll define the `HelloMacro` trait and its associated function: + ++ +```rust,noplayground +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-20-impl-hellomacro-for-pancakes/hello_macro/src/lib.rs}} +``` + + + +We have a trait and its function. At this point, our crate user could implement +the trait to achieve the desired functionality, like so: + +```rust,ignore +{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-20-impl-hellomacro-for-pancakes/pancakes/src/main.rs}} +``` + +However, they would need to write the implementation block for each type they +wanted to use with `hello_macro`; we want to spare them from having to do this +work. + +Additionally, we can’t yet provide the `hello_macro` function with default +implementation that will print the name of the type the trait is implemented +on: Rust doesn’t have reflection capabilities, so it can’t look up the type’s +name at runtime. We need a macro to generate code at compile time. + +The next step is to define the procedural macro. At the time of this writing, +procedural macros need to be in their own crate. Eventually, this restriction +might be lifted. The convention for structuring crates and macro crates is as +follows: for a crate named `foo`, a custom derive procedural macro crate is +called `foo_derive`. Let’s start a new crate called `hello_macro_derive` inside +our `hello_macro` project: + +```console +$ cargo new hello_macro_derive --lib +``` + +Our two crates are tightly related, so we create the procedural macro crate +within the directory of our `hello_macro` crate. If we change the trait +definition in `hello_macro`, we’ll have to change the implementation of the +procedural macro in `hello_macro_derive` as well. The two crates will need to +be published separately, and programmers using these crates will need to add +both as dependencies and bring them both into scope. We could instead have the +`hello_macro` crate use `hello_macro_derive` as a dependency and re-export the +procedural macro code. However, the way we’ve structured the project makes it +possible for programmers to use `hello_macro` even if they don’t want the +`derive` functionality. + +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 you’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`: + ++ +```toml +{{#include ../listings/ch20-advanced-features/listing-20-32/hello_macro/hello_macro_derive/Cargo.toml:6:12}} +``` + + + +To start defining the procedural macro, place the code in Listing 20-32 into +your _src/lib.rs_ file for the `hello_macro_derive` crate. Note that this code +won’t compile until we add a definition for the `impl_hello_macro` function. + ++ +```rust,ignore,does_not_compile +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-32/hello_macro/hello_macro_derive/src/lib.rs}} +``` + + + +Notice that we’ve split the code into the `hello_macro_derive` function, which +is responsible for parsing the `TokenStream`, and the `impl_hello_macro` +function, which is responsible for transforming the syntax tree: this makes +writing a procedural macro more convenient. The code in the outer function +(`hello_macro_derive` 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 (`impl_hello_macro` in this case) will be different +depending on your procedural macro’s purpose. + +We’ve introduced three new crates: `proc_macro`, [`syn`], and [`quote`]. The +`proc_macro` crate comes with Rust, so we didn’t need to add that to the +dependencies in _Cargo.toml_. The `proc_macro` crate is the compiler’s API that +allows us to read and manipulate Rust code from our code. + +The `syn` crate parses Rust code from a string into a data structure that we +can perform operations on. The `quote` crate turns `syn` 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. + +The `hello_macro_derive` function will be called when a user of our library +specifies `#[derive(HelloMacro)]` on a type. This is possible because we’ve +annotated the `hello_macro_derive` function here with `proc_macro_derive` and +specified the name `HelloMacro`, which matches our trait name; this is the +convention most procedural macros follow. + +The `hello_macro_derive` function first converts the `input` from a +`TokenStream` to a data structure that we can then interpret and perform +operations on. This is where `syn` comes into play. The `parse` function in +`syn` takes a `TokenStream` and returns a `DeriveInput` struct representing the +parsed Rust code. Listing 20-33 shows the relevant parts of the `DeriveInput` +struct we get from parsing the `struct Pancakes;` string: + ++ +```rust,ignore +DeriveInput { + // --snip-- + + ident: Ident { + ident: "Pancakes", + span: #0 bytes(95..103) + }, + data: Struct( + DataStruct { + struct_token: Struct, + fields: Unit, + semi_token: Some( + Semi + ) + } + ) +} +``` + + + +The fields of this struct show that the Rust code we’ve parsed is a unit struct +with the `ident` (identifier, meaning the name) of `Pancakes`. There are more +fields on this struct for describing all sorts of Rust code; check the [`syn` +documentation for `DeriveInput`][syn-docs] for more information. + +Soon we’ll define the `impl_hello_macro` function, which is where we’ll build +the new Rust code we want to include. But before we do, note that the output +for our derive macro is also a `TokenStream`. The returned `TokenStream` is +added to the code that our crate users write, so when they compile their crate, +they’ll get the extra functionality that we provide in the modified +`TokenStream`. + +You might have noticed that we’re calling `unwrap` to cause the +`hello_macro_derive` function to panic if the call to the `syn::parse` function +fails here. It’s necessary for our procedural macro to panic on errors because +`proc_macro_derive` functions must return `TokenStream` rather than `Result` to +conform to the procedural macro API. We’ve simplified this example by using +`unwrap`; in production code, you should provide more specific error messages +about what went wrong by using `panic!` or `expect`. + +Now that we have the code to turn the annotated Rust code from a `TokenStream` +into a `DeriveInput` instance, let’s generate the code that implements the +`HelloMacro` trait on the annotated type, as shown in Listing 20-34. + ++ +```rust,ignore +{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-34/hello_macro/hello_macro_derive/src/lib.rs:here}} +``` + + + +We get an `Ident` struct instance containing the name (identifier) of the +annotated type using `ast.ident`. The struct in Listing 20-33 shows that when +we run the `impl_hello_macro` function on the code in Listing 20-31, the +`ident` we get will have the `ident` field with a value of `"Pancakes"`. Thus, +the `name` variable in Listing 20-34 will contain an `Ident` struct instance +that, when printed, will be the string `"Pancakes"`, the name of the struct in +Listing 20-31. + +The `quote!` macro lets us define the Rust code that we want to return. The +compiler expects something different to the direct result of the `quote!` +macro’s execution, so we need to convert it to a `TokenStream`. We do this by +calling the `into` method, which consumes this intermediate representation and +returns a value of the required `TokenStream` type. + +The `quote!` macro also provides some very cool templating mechanics: we can +enter `#name`, and `quote!` will replace it with the value in the variable +`name`. You can even do some repetition similar to the way regular macros work. +Check out [the `quote` crate’s docs][quote-docs] for a thorough introduction. + +We want our procedural macro to generate an implementation of our `HelloMacro` +trait for the type the user annotated, which we can get by using `#name`. The +trait implementation has the 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!`, macros which evaluate the expression and then turn the result into +a `String`. There is a possibility that the `#name` input might be an +expression to print literally, so we use `stringify!`. Using `stringify!` also +saves an allocation by converting `#name` to a string literal at compile time. + +At this point, `cargo build` should complete successfully in both `hello_macro` +and `hello_macro_derive`. Let’s hook up these crates to the code in Listing +20-31 to see the procedural macro in action! Create a new binary project in +your _projects_ directory using `cargo new pancakes`. We need to add +`hello_macro` and `hello_macro_derive` as dependencies in the `pancakes` +crate’s _Cargo.toml_. If you’re publishing your versions of `hello_macro` and +`hello_macro_derive` to [crates.io](https://crates.io/), they would be regular +dependencies; if not, you can specify them as `path` dependencies as follows: + +```toml +{{#include ../listings/ch20-advanced-features/no-listing-21-pancakes/pancakes/Cargo.toml:7:9}} +``` + +Put the code in Listing 20-31 into _src/main.rs_, and run `cargo run`: it +should print `Hello, Macro! My name is Pancakes!` The implementation of the +`HelloMacro` trait from the procedural macro was included without the +`pancakes` crate needing to implement it; the `#[derive(HelloMacro)]` added the +trait implementation. + +Next, let’s explore how the other kinds of procedural macros differ from custom +derive macros. + +### Attribute-like macros + +Attribute-like macros are similar to custom derive macros, but instead of +generating code for the `derive` attribute, they allow you to create new +attributes. They’re also more flexible: `derive` only works for structs and +enums; attributes can be applied to other items as well, such as functions. +Here’s an example of using an attribute-like macro: say you have an attribute +named `route` that annotates functions when using a web application framework: + +```rust,ignore +#[route(GET, "/")] +fn index() { +``` + +This `#[route]` attribute would be defined by the framework as a procedural +macro. The signature of the macro definition function would look like this: + +```rust,ignore +#[proc_macro_attribute] +pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream { +``` + +Here, we have two parameters of type `TokenStream`. The first is for the +contents of the attribute: the `GET, "/"` part. The second is the body of the +item the attribute is attached to: in this case, `fn index() {}` and the rest +of the function’s body. + +Other than that, attribute-like macros work the same way as custom derive +macros: you create a crate with the `proc-macro` crate type and implement a +function that generates the code you want! + +### Function-like macros + +Function-like macros define macros that look like function calls. Similarly to +`macro_rules!` macros, they’re more flexible than functions; for example, they +can take an unknown number of arguments. However, `macro_rules!` macros can be +defined only using the match-like syntax we discussed in the section +[“Declarative Macros with `macro_rules!` for General +Metaprogramming”][decl] earlier. Function-like macros take a +`TokenStream` parameter and their definition manipulates that `TokenStream` +using Rust code as the other two types of procedural macros do. An example of a +function-like macro is an `sql!` macro that might be called like so: + +```rust,ignore +let sql = sql!(SELECT * FROM posts WHERE id=1); +``` + +This macro would parse the SQL statement inside it and check that it’s +syntactically correct, which is much more complex processing than a +`macro_rules!` macro can do. The `sql!` macro would be defined like this: + +```rust,ignore +#[proc_macro] +pub fn sql(input: TokenStream) -> TokenStream { +``` + +This definition is similar to the custom derive macro’s signature: we receive +the tokens that are inside the parentheses and return the code we wanted to +generate. + +## Summary + +Whew! Now you have some Rust features in your toolbox that you likely won’t use +often, but you’ll know they’re available in very particular circumstances. +We’ve introduced several complex topics so that when you encounter them in +error message suggestions or in other peoples’ code, you’ll be able to +recognize these concepts and syntax. Use this chapter as a reference to guide +you to solutions. + +Next, we’ll put everything we’ve discussed throughout the book into practice +and do one more project! + +[ref]: ../reference/macros-by-example.html +[tlborm]: https://veykril.github.io/tlborm/ +[`syn`]: https://crates.io/crates/syn +[`quote`]: https://crates.io/crates/quote +[syn-docs]: https://docs.rs/syn/2.0/syn/struct.DeriveInput.html +[quote-docs]: https://docs.rs/quote +[decl]: #declarative-macros-with-macro_rules-for-general-metaprogramming diff --git a/src/ch20-06-macros.md b/src/ch20-06-macros.md index edc5fc4c8f..19921943ec 100644 --- a/src/ch20-06-macros.md +++ b/src/ch20-06-macros.md @@ -1,514 +1 @@ -## Macros - -We’ve used macros like `println!` throughout this book, but we haven’t fully -explored what a macro is and how it works. The term _macro_ refers to a family -of features in Rust: _declarative_ macros with `macro_rules!` and three kinds -of _procedural_ macros: - -- Custom `#[derive]` macros that specify code added with the `derive` attribute - used on structs and enums -- Attribute-like macros that define custom attributes usable on any item -- Function-like macros that look like function calls but operate on the tokens - specified as their argument - -We’ll talk about each of these in turn, but first, let’s look at why we even -need macros when we already have 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 Appendix C, we discuss the `derive` -attribute, which generates an implementation of various traits for you. We’ve -also used the `println!` and `vec!` macros throughout the book. All of these -macros _expand_ to produce more code than the code you’ve written manually. - -Metaprogramming is useful for reducing the amount of code you have to write and -maintain, which is also one of the roles of functions. However, macros have -some additional powers that functions don’t. - -A function signature must declare the number and type of parameters the -function has. Macros, on the other hand, can take a variable number of -parameters: we can call `println!("hello")` with one argument or -`println!("hello {}", name)` with two arguments. Also, macros are expanded -before the compiler interprets the meaning of the code, so a macro can, for -example, implement a trait on a given type. A function can’t, because it gets -called at runtime and a trait needs to be implemented at compile time. - -The downside to implementing a macro instead of a function is that macro -definitions are more complex than function definitions because you’re 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 important difference between macros and functions is that you must -define macros or bring them into scope _before_ you call them in a file, as -opposed to functions you can define anywhere and call anywhere. - -### Declarative Macros with `macro_rules!` for General Metaprogramming - -The most widely used form of macros in Rust is the _declarative macro_. 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 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. - -To define a macro, you use the `macro_rules!` construct. Let’s explore how to -use `macro_rules!` by looking at how the `vec!` macro is defined. Chapter 8 -covered how we can use the `vec!` macro to create a new vector with particular -values. For example, the following macro creates a new vector containing three -integers: - -```rust -let v: Vec = vec![1, 2, 3]; -``` - -We could also use the `vec!` macro to make a vector of two integers or a vector -of five string slices. We wouldn’t be able to use a function to do the same -because we wouldn’t know the number or type of values up front. - -Listing 20-29 shows a slightly simplified definition of the `vec!` macro. - -- -```rust,noplayground -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-29/src/lib.rs}} -``` - - - -> Note: The actual definition of the `vec!` macro in the standard library -> includes code to preallocate the correct amount of memory up front. That code -> is an optimization that we don’t include here to make the example simpler. - -The `#[macro_export]` 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 can’t be brought into scope. - -We then start the macro definition with `macro_rules!` and the name of the -macro we’re defining _without_ the exclamation mark. The name, in this case -`vec`, is followed by curly brackets denoting the body of the macro definition. - -The structure in the `vec!` body is similar to the structure of a `match` -expression. Here we have one arm with the pattern `( $( $x:expr ),* )`, -followed by `=>` and the block of code associated with this pattern. If the -pattern matches, the associated block of code will be emitted. Given that this -is the only pattern in this macro, there is only one valid way to match; any -other pattern will result in an error. More complex macros will have more than -one arm. - -Valid pattern syntax in macro definitions is different than the pattern syntax -covered in Chapter 19 because macro patterns are matched against Rust code -structure rather than values. Let’s walk through what the pattern pieces in -Listing 20-29 mean; for the full macro pattern syntax, see the [Rust -Reference][ref]. - -First, we use a set of parentheses to encompass the whole pattern. We use a -dollar sign (`$`) to declare a variable in the macro system that will contain -the Rust code matching the pattern. The dollar sign makes it clear this is a -macro variable as opposed to a regular Rust variable. Next comes a set of -parentheses that captures values that match the pattern within the parentheses -for use in the replacement code. Within `$()` is `$x:expr`, which matches any -Rust expression and gives the expression the name `$x`. - -The comma following `$()` indicates that a literal comma separator character -must appear between each instance of the code that matches the code within -`$()`. The `*` 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: -`temp_vec.push()` within `$()*` is generated for each part that matches `$()` -in the pattern zero or more times depending on how many times the pattern -matches. The `$x` is replaced with each expression matched. When we call this -macro with `vec![1, 2, 3];`, the code generated that replaces this macro call -will be the following: - -```rust,ignore -{ - let mut temp_vec = Vec::new(); - temp_vec.push(1); - temp_vec.push(2); - temp_vec.push(3); - temp_vec -} -``` - -We’ve 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. - -To learn more about how to write macros, consult the online documentation or -other resources, such as [“The Little Book of Rust Macros”][tlborm] started by -Daniel Keep and continued by Lukas Wirth. - -### Procedural Macros for Generating Code from Attributes - -The second form of macros is the _procedural macro_, which acts more like a -function (and is 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. The three kinds of procedural macros are custom derive, -attribute-like, and function-like, and all work in a similar fashion. - -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. In Listing 20-30, we show how to define a -procedural macro, where `some_attribute` is a placeholder for using a specific -macro variety. - -- -```rust,ignore -use proc_macro; - -#[some_attribute] -pub fn some_name(input: TokenStream) -> TokenStream { -} -``` - - - -The function that defines a procedural macro takes a `TokenStream` as an input -and produces a `TokenStream` as an output. The `TokenStream` type is defined by -the `proc_macro` 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 `TokenStream`, and the code the macro produces -is the output `TokenStream`. The function also has an attribute attached to it -that specifies which kind of procedural macro we’re creating. We can have -multiple kinds of procedural macros in the same crate. - -Let’s look at the different kinds of procedural macros. We’ll start with a -custom derive macro and then explain the small dissimilarities that make the -other forms different. - -### How to Write a Custom `derive` Macro - -Let’s create a crate named `hello_macro` that defines a trait named -`HelloMacro` with one associated function named `hello_macro`. Rather than -making our users implement the `HelloMacro` trait for each of their types, -we’ll provide a procedural macro so users can annotate their type with -`#[derive(HelloMacro)]` to get a default implementation of the `hello_macro` -function. The default implementation will print `Hello, Macro! My name is -TypeName!` where `TypeName` is the name of the type on which this trait has -been defined. In other words, we’ll write a crate that enables another -programmer to write code like Listing 20-31 using our crate. - -- -```rust,ignore,does_not_compile -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-31/src/main.rs}} -``` - - - -This code will print `Hello, Macro! My name is Pancakes!` when we’re done. The -first step is to make a new library crate, like this: - -```console -$ cargo new hello_macro --lib -``` - -Next, we’ll define the `HelloMacro` trait and its associated function: - -- -```rust,noplayground -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-20-impl-hellomacro-for-pancakes/hello_macro/src/lib.rs}} -``` - - - -We have a trait and its function. At this point, our crate user could implement -the trait to achieve the desired functionality, like so: - -```rust,ignore -{{#rustdoc_include ../listings/ch20-advanced-features/no-listing-20-impl-hellomacro-for-pancakes/pancakes/src/main.rs}} -``` - -However, they would need to write the implementation block for each type they -wanted to use with `hello_macro`; we want to spare them from having to do this -work. - -Additionally, we can’t yet provide the `hello_macro` function with default -implementation that will print the name of the type the trait is implemented -on: Rust doesn’t have reflection capabilities, so it can’t look up the type’s -name at runtime. We need a macro to generate code at compile time. - -The next step is to define the procedural macro. At the time of this writing, -procedural macros need to be in their own crate. Eventually, this restriction -might be lifted. The convention for structuring crates and macro crates is as -follows: for a crate named `foo`, a custom derive procedural macro crate is -called `foo_derive`. Let’s start a new crate called `hello_macro_derive` inside -our `hello_macro` project: - -```console -$ cargo new hello_macro_derive --lib -``` - -Our two crates are tightly related, so we create the procedural macro crate -within the directory of our `hello_macro` crate. If we change the trait -definition in `hello_macro`, we’ll have to change the implementation of the -procedural macro in `hello_macro_derive` as well. The two crates will need to -be published separately, and programmers using these crates will need to add -both as dependencies and bring them both into scope. We could instead have the -`hello_macro` crate use `hello_macro_derive` as a dependency and re-export the -procedural macro code. However, the way we’ve structured the project makes it -possible for programmers to use `hello_macro` even if they don’t want the -`derive` functionality. - -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 you’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`: - -- -```toml -{{#include ../listings/ch20-advanced-features/listing-20-32/hello_macro/hello_macro_derive/Cargo.toml:6:12}} -``` - - - -To start defining the procedural macro, place the code in Listing 20-32 into -your _src/lib.rs_ file for the `hello_macro_derive` crate. Note that this code -won’t compile until we add a definition for the `impl_hello_macro` function. - -- -```rust,ignore,does_not_compile -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-32/hello_macro/hello_macro_derive/src/lib.rs}} -``` - - - -Notice that we’ve split the code into the `hello_macro_derive` function, which -is responsible for parsing the `TokenStream`, and the `impl_hello_macro` -function, which is responsible for transforming the syntax tree: this makes -writing a procedural macro more convenient. The code in the outer function -(`hello_macro_derive` 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 (`impl_hello_macro` in this case) will be different -depending on your procedural macro’s purpose. - -We’ve introduced three new crates: `proc_macro`, [`syn`], and [`quote`]. The -`proc_macro` crate comes with Rust, so we didn’t need to add that to the -dependencies in _Cargo.toml_. The `proc_macro` crate is the compiler’s API that -allows us to read and manipulate Rust code from our code. - -The `syn` crate parses Rust code from a string into a data structure that we -can perform operations on. The `quote` crate turns `syn` 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. - -The `hello_macro_derive` function will be called when a user of our library -specifies `#[derive(HelloMacro)]` on a type. This is possible because we’ve -annotated the `hello_macro_derive` function here with `proc_macro_derive` and -specified the name `HelloMacro`, which matches our trait name; this is the -convention most procedural macros follow. - -The `hello_macro_derive` function first converts the `input` from a -`TokenStream` to a data structure that we can then interpret and perform -operations on. This is where `syn` comes into play. The `parse` function in -`syn` takes a `TokenStream` and returns a `DeriveInput` struct representing the -parsed Rust code. Listing 20-33 shows the relevant parts of the `DeriveInput` -struct we get from parsing the `struct Pancakes;` string: - -- -```rust,ignore -DeriveInput { - // --snip-- - - ident: Ident { - ident: "Pancakes", - span: #0 bytes(95..103) - }, - data: Struct( - DataStruct { - struct_token: Struct, - fields: Unit, - semi_token: Some( - Semi - ) - } - ) -} -``` - - - -The fields of this struct show that the Rust code we’ve parsed is a unit struct -with the `ident` (identifier, meaning the name) of `Pancakes`. There are more -fields on this struct for describing all sorts of Rust code; check the [`syn` -documentation for `DeriveInput`][syn-docs] for more information. - -Soon we’ll define the `impl_hello_macro` function, which is where we’ll build -the new Rust code we want to include. But before we do, note that the output -for our derive macro is also a `TokenStream`. The returned `TokenStream` is -added to the code that our crate users write, so when they compile their crate, -they’ll get the extra functionality that we provide in the modified -`TokenStream`. - -You might have noticed that we’re calling `unwrap` to cause the -`hello_macro_derive` function to panic if the call to the `syn::parse` function -fails here. It’s necessary for our procedural macro to panic on errors because -`proc_macro_derive` functions must return `TokenStream` rather than `Result` to -conform to the procedural macro API. We’ve simplified this example by using -`unwrap`; in production code, you should provide more specific error messages -about what went wrong by using `panic!` or `expect`. - -Now that we have the code to turn the annotated Rust code from a `TokenStream` -into a `DeriveInput` instance, let’s generate the code that implements the -`HelloMacro` trait on the annotated type, as shown in Listing 20-34. - -- -```rust,ignore -{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-34/hello_macro/hello_macro_derive/src/lib.rs:here}} -``` - - - -We get an `Ident` struct instance containing the name (identifier) of the -annotated type using `ast.ident`. The struct in Listing 20-33 shows that when -we run the `impl_hello_macro` function on the code in Listing 20-31, the -`ident` we get will have the `ident` field with a value of `"Pancakes"`. Thus, -the `name` variable in Listing 20-34 will contain an `Ident` struct instance -that, when printed, will be the string `"Pancakes"`, the name of the struct in -Listing 20-31. - -The `quote!` macro lets us define the Rust code that we want to return. The -compiler expects something different to the direct result of the `quote!` -macro’s execution, so we need to convert it to a `TokenStream`. We do this by -calling the `into` method, which consumes this intermediate representation and -returns a value of the required `TokenStream` type. - -The `quote!` macro also provides some very cool templating mechanics: we can -enter `#name`, and `quote!` will replace it with the value in the variable -`name`. You can even do some repetition similar to the way regular macros work. -Check out [the `quote` crate’s docs][quote-docs] for a thorough introduction. - -We want our procedural macro to generate an implementation of our `HelloMacro` -trait for the type the user annotated, which we can get by using `#name`. The -trait implementation has the 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!`, macros which evaluate the expression and then turn the result into -a `String`. There is a possibility that the `#name` input might be an -expression to print literally, so we use `stringify!`. Using `stringify!` also -saves an allocation by converting `#name` to a string literal at compile time. - -At this point, `cargo build` should complete successfully in both `hello_macro` -and `hello_macro_derive`. Let’s hook up these crates to the code in Listing -20-31 to see the procedural macro in action! Create a new binary project in -your _projects_ directory using `cargo new pancakes`. We need to add -`hello_macro` and `hello_macro_derive` as dependencies in the `pancakes` -crate’s _Cargo.toml_. If you’re publishing your versions of `hello_macro` and -`hello_macro_derive` to [crates.io](https://crates.io/), they would be regular -dependencies; if not, you can specify them as `path` dependencies as follows: - -```toml -{{#include ../listings/ch20-advanced-features/no-listing-21-pancakes/pancakes/Cargo.toml:7:9}} -``` - -Put the code in Listing 20-31 into _src/main.rs_, and run `cargo run`: it -should print `Hello, Macro! My name is Pancakes!` The implementation of the -`HelloMacro` trait from the procedural macro was included without the -`pancakes` crate needing to implement it; the `#[derive(HelloMacro)]` added the -trait implementation. - -Next, let’s explore how the other kinds of procedural macros differ from custom -derive macros. - -### Attribute-like macros - -Attribute-like macros are similar to custom derive macros, but instead of -generating code for the `derive` attribute, they allow you to create new -attributes. They’re also more flexible: `derive` only works for structs and -enums; attributes can be applied to other items as well, such as functions. -Here’s an example of using an attribute-like macro: say you have an attribute -named `route` that annotates functions when using a web application framework: - -```rust,ignore -#[route(GET, "/")] -fn index() { -``` - -This `#[route]` attribute would be defined by the framework as a procedural -macro. The signature of the macro definition function would look like this: - -```rust,ignore -#[proc_macro_attribute] -pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream { -``` - -Here, we have two parameters of type `TokenStream`. The first is for the -contents of the attribute: the `GET, "/"` part. The second is the body of the -item the attribute is attached to: in this case, `fn index() {}` and the rest -of the function’s body. - -Other than that, attribute-like macros work the same way as custom derive -macros: you create a crate with the `proc-macro` crate type and implement a -function that generates the code you want! - -### Function-like macros - -Function-like macros define macros that look like function calls. Similarly to -`macro_rules!` macros, they’re more flexible than functions; for example, they -can take an unknown number of arguments. However, `macro_rules!` macros can be -defined only using the match-like syntax we discussed in the section -[“Declarative Macros with `macro_rules!` for General -Metaprogramming”][decl] earlier. Function-like macros take a -`TokenStream` parameter and their definition manipulates that `TokenStream` -using Rust code as the other two types of procedural macros do. An example of a -function-like macro is an `sql!` macro that might be called like so: - -```rust,ignore -let sql = sql!(SELECT * FROM posts WHERE id=1); -``` - -This macro would parse the SQL statement inside it and check that it’s -syntactically correct, which is much more complex processing than a -`macro_rules!` macro can do. The `sql!` macro would be defined like this: - -```rust,ignore -#[proc_macro] -pub fn sql(input: TokenStream) -> TokenStream { -``` - -This definition is similar to the custom derive macro’s signature: we receive -the tokens that are inside the parentheses and return the code we wanted to -generate. - -## Summary - -Whew! Now you have some Rust features in your toolbox that you likely won’t use -often, but you’ll know they’re available in very particular circumstances. -We’ve introduced several complex topics so that when you encounter them in -error message suggestions or in other peoples’ code, you’ll be able to -recognize these concepts and syntax. Use this chapter as a reference to guide -you to solutions. - -Next, we’ll put everything we’ve discussed throughout the book into practice -and do one more project! - -[ref]: ../reference/macros-by-example.html -[tlborm]: https://veykril.github.io/tlborm/ -[`syn`]: https://crates.io/crates/syn -[`quote`]: https://crates.io/crates/quote -[syn-docs]: https://docs.rs/syn/2.0/syn/struct.DeriveInput.html -[quote-docs]: https://docs.rs/quote -[decl]: #declarative-macros-with-macro_rules-for-general-metaprogramming +# Macros diff --git a/src/ch21-02-multithreaded.md b/src/ch21-02-multithreaded.md index c2e7865ab5..0a5ef9f15e 100644 --- a/src/ch21-02-multithreaded.md +++ b/src/ch21-02-multithreaded.md @@ -703,7 +703,7 @@ let` (and `if let` and `match`) does not drop temporary values until the end of the associated block. In Listing 21-21, the lock remains held for the duration of the call to `job()`, meaning other workers cannot receive jobs. -[creating-type-synonyms-with-type-aliases]: ch20-04-advanced-types.html#creating-type-synonyms-with-type-aliases +[creating-type-synonyms-with-type-aliases]: ch20-03-advanced-types.html#creating-type-synonyms-with-type-aliases [integer-types]: ch03-02-data-types.html#integer-types [fn-traits]: ch13-01-closures.html#moving-captured-values-out-of-the-closure-and-the-fn-traits [builder]: ../std/thread/struct.Builder.html