From bfcebbb2ca3617657826eea988efd268aba08f3b Mon Sep 17 00:00:00 2001 From: Chris Krycho Date: Wed, 9 Oct 2024 09:28:11 -0600 Subject: [PATCH] Adopt for Ch. 20 --- src/ch20-01-unsafe-rust.md | 54 +++++++------ src/ch20-03-advanced-traits.md | 76 +++++++++---------- src/ch20-04-advanced-types.md | 14 ++-- ...ch20-05-advanced-functions-and-closures.md | 5 +- src/ch20-06-macros.md | 38 +++++----- 5 files changed, 95 insertions(+), 92 deletions(-) diff --git a/src/ch20-01-unsafe-rust.md b/src/ch20-01-unsafe-rust.md index dfdef6c39..ae4e674f1 100644 --- a/src/ch20-01-unsafe-rust.md +++ b/src/ch20-01-unsafe-rust.md @@ -91,11 +91,13 @@ interface with another language or hardware where Rust’s guarantees don’t ap Listing 20-1 shows how to create an immutable and a mutable raw pointer from references. ++ ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-01/src/main.rs:here}} ``` -Listing 20-1: Creating raw pointers from references + Notice that we don’t include the `unsafe` keyword in this code. We can create raw pointers in safe code; we just can’t dereference raw pointers outside an @@ -115,23 +117,25 @@ so there is no memory access, or the program might error with a segmentation fault. Usually, there is no good reason to write code like this, but it is possible. ++ ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-02/src/main.rs:here}} ``` -Listing 20-2: Creating a raw pointer to an arbitrary -memory address + Recall that we can create raw pointers in safe code, but we can’t *dereference* raw pointers and read the data being pointed to. In Listing 20-3, we use the dereference operator `*` on a raw pointer that requires an `unsafe` block. ++ ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-03/src/main.rs:here}} ``` -Listing 20-3: Dereferencing raw pointers within an -`unsafe` block + Creating a pointer does no harm; it’s only when we try to access the value that it points at that we might end up dealing with an invalid value. @@ -196,24 +200,26 @@ we might implement it. This safe method is defined on mutable slices: it takes one slice and makes it two by splitting the slice at the index given as an argument. Listing 20-4 shows how to use `split_at_mut`. ++ ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-04/src/main.rs:here}} ``` -Listing 20-4: Using the safe `split_at_mut` -function + We can’t implement this function using only safe Rust. An attempt might look something like Listing 20-5, which won’t compile. For simplicity, we’ll implement `split_at_mut` as a function rather than a method and only for slices of `i32` values rather than for a generic type `T`. ++ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-05/src/main.rs:here}} ``` -Listing 20-5: An attempted implementation of -`split_at_mut` using only safe Rust + This function first gets the total length of the slice. Then it asserts that the index given as a parameter is within the slice by checking whether it’s @@ -240,12 +246,13 @@ know code is okay, but Rust doesn’t, it’s time to reach for unsafe code. Listing 20-6 shows how to use an `unsafe` block, a raw pointer, and some calls to unsafe functions to make the implementation of `split_at_mut` work. ++ ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-06/src/main.rs:here}} ``` -Listing 20-6: Using unsafe code in the implementation of -the `split_at_mut` function + Recall from [“The Slice Type”][the-slice-type] section in Chapter 4 that slices are a pointer to some data and the length of the slice. @@ -282,12 +289,13 @@ In contrast, the use of `slice::from_raw_parts_mut` in Listing 20-7 would likely crash when the slice is used. This code takes an arbitrary memory location and creates a slice 10,000 items long. ++ ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-07/src/main.rs:here}} ``` -Listing 20-7: Creating a slice from an arbitrary memory -location + We don’t own the memory at this arbitrary location, and there is no guarantee that the slice this code creates contains valid `i32` values. Attempting to use @@ -307,14 +315,13 @@ always unsafe to call from Rust code. The reason is that other languages don’t enforce Rust’s rules and guarantees, and Rust can’t check them, so responsibility falls on the programmer to ensure safety. -Filename: src/main.rs + ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-08/src/main.rs}} ``` -Listing 20-8: Declaring and calling an `extern` function -defined in another language + Within the `extern "C"` block, we list the names and signatures of external functions from another language we want to call. The `"C"` part defines which @@ -357,14 +364,13 @@ In Rust, global variables are called *static* variables. Listing 20-9 shows an example declaration and use of a static variable with a string slice as a value. -Filename: src/main.rs + ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-09/src/main.rs}} ``` -Listing 20-9: Defining and using an immutable static -variable + Static variables are similar to constants, which we discussed in the [“Differences Between Variables and @@ -383,14 +389,13 @@ variables can be mutable. Accessing and modifying mutable static variables is *unsafe*. Listing 20-10 shows how to declare, access, and modify a mutable static variable named `COUNTER`. -Filename: src/main.rs + ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-10/src/main.rs}} ``` -Listing 20-10: Reading from or writing to a mutable -static variable is unsafe + As with regular variables, we specify mutability using the `mut` keyword. Any code that reads or writes from `COUNTER` must be within an `unsafe` block. This @@ -412,12 +417,13 @@ declare that a trait is `unsafe` by adding the `unsafe` keyword before `trait` and marking the implementation of the trait as `unsafe` too, as shown in Listing 20-11. ++ ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-11/src/main.rs}} ``` -Listing 20-11: Defining and implementing an unsafe -trait + By using `unsafe impl`, we’re promising that we’ll uphold the invariants that the compiler can’t verify. diff --git a/src/ch20-03-advanced-traits.md b/src/ch20-03-advanced-traits.md index 7dc333e78..e411dd57d 100644 --- a/src/ch20-03-advanced-traits.md +++ b/src/ch20-03-advanced-traits.md @@ -25,12 +25,13 @@ 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-12. ++ ```rust,noplayground {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-12/src/lib.rs}} ``` -Listing 20-12: The definition of the `Iterator` trait -that has an associated type `Item` + The type `Item` is a placeholder, and the `next` method’s definition shows that it will return values of type `Option`. Implementors of the @@ -43,21 +44,24 @@ 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`: -Filename: src/lib.rs + ```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-13? ++ ```rust,noplayground {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-13/src/lib.rs}} ``` -Listing 20-13: A hypothetical definition of the -`Iterator` trait using generics + The difference is that when using generics, as in Listing 20-13, we must annotate the types in each implementation; because we can also implement @@ -98,14 +102,13 @@ example, in Listing 20-14 we overload the `+` operator to add two `Point` instances together. We do this by implementing the `Add` trait on a `Point` struct: -Filename: src/main.rs + ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-14/src/main.rs}} ``` -Listing 20-14: Implementing the `Add` trait to overload -the `+` operator for `Point` instances + 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 @@ -144,14 +147,13 @@ Pattern to Implement External Traits on External Types”][newtype] diff --git a/src/ch20-05-advanced-functions-and-closures.md b/src/ch20-05-advanced-functions-and-closures.md index ef6a6e46f..70385af32 100644 --- a/src/ch20-05-advanced-functions-and-closures.md +++ b/src/ch20-05-advanced-functions-and-closures.md @@ -22,14 +22,13 @@ 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`. -Filename: src/main.rs + ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-27/src/main.rs}} ``` -Listing 20-27: Using the `fn` type to accept a function -pointer as an argument + 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 diff --git a/src/ch20-06-macros.md b/src/ch20-06-macros.md index 2406942df..7900a015b 100644 --- a/src/ch20-06-macros.md +++ b/src/ch20-06-macros.md @@ -75,14 +75,13 @@ because we wouldn’t know the number or type of values up front. Listing 20-28 shows a slightly simplified definition of the `vec!` macro. -Filename: src/lib.rs + ```rust,noplayground {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-28/src/lib.rs}} ``` -Listing 20-28: A simplified version of the `vec!` macro -definition + > Note: The actual definition of the `vec!` macro in the standard library > includes code to preallocate the correct amount of memory up front. That code @@ -164,7 +163,7 @@ to eliminate in the future. In Listing 20-29, we show how to define a procedural macro, where `some_attribute` is a placeholder for using a specific macro variety. -Filename: src/lib.rs + ```rust,ignore use proc_macro; @@ -174,8 +173,7 @@ pub fn some_name(input: TokenStream) -> TokenStream { } ``` -Listing 20-29: An example of defining a procedural -macro + 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 @@ -202,14 +200,13 @@ 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-30 using our crate. -Filename: src/main.rs + ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-30/src/main.rs}} ``` -Listing 20-30: The code a user of our crate will be able -to write when using our procedural macro + This code will print `Hello, Macro! My name is Pancakes!` when we’re done. The first step is to make a new library crate, like this: @@ -220,12 +217,14 @@ $ cargo new hello_macro --lib Next, we’ll define the `HelloMacro` trait and its associated function: -Filename: src/lib.rs + ```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: @@ -269,24 +268,25 @@ We’ll also need functionality from the `syn` and `quote` crates, as you’ll s in a moment, so we need to add them as dependencies. Add the following to the *Cargo.toml* file for `hello_macro_derive`: -Filename: hello_macro_derive/Cargo.toml + ```toml {{#include ../listings/ch20-advanced-features/listing-20-31/hello_macro/hello_macro_derive/Cargo.toml:6:12}} ``` + + To start defining the procedural macro, place the code in Listing 20-31 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. -Filename: hello_macro_derive/src/lib.rs + ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-31/hello_macro/hello_macro_derive/src/lib.rs}} ``` -Listing 20-31: Code that most procedural macro crates -will require in order to process Rust code + 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` @@ -321,6 +321,8 @@ operations on. This is where `syn` comes into play. The `parse` function in parsed Rust code. Listing 20-32 shows the relevant parts of the `DeriveInput` struct we get from parsing the `struct Pancakes;` string: ++ ```rust,ignore DeriveInput { // --snip-- @@ -341,8 +343,7 @@ DeriveInput { } ``` -Listing 20-32: The `DeriveInput` instance we get when -parsing the code that has the macro’s attribute in Listing 20-30 + 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 @@ -368,14 +369,13 @@ 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-33. -Filename: hello_macro_derive/src/lib.rs + ```rust,ignore {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-33/hello_macro/hello_macro_derive/src/lib.rs:here}} ``` -Listing 20-33: Implementing the `HelloMacro` trait using -the parsed Rust code + We get an `Ident` struct instance containing the name (identifier) of the annotated type using `ast.ident`. The struct in Listing 20-32 shows that when