Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use <Listing> for final chapters (after restructuring) #4060

Merged
merged 4 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions src/ch18-01-what-is-oo.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,30 +44,29 @@ on demand whenever anyone needs it. In other words, `AveragedCollection` will
cache the calculated average for us. Listing 18-1 has the definition of the
`AveragedCollection` struct:

<span class="filename">Filename: src/lib.rs</span>
<Listing number="18-1" file-name="src/lib.rs" caption="Listing 18-1: An `AveragedCollection` struct that maintains a list of integers and the average of the items in the collection">

```rust,noplayground
{{#rustdoc_include ../listings/ch18-oop/listing-18-01/src/lib.rs}}
```

<span class="caption">Listing 18-1: An `AveragedCollection` struct that
maintains a list of integers and the average of the items in the
collection</span>
</Listing>

The struct is marked `pub` so that other code can use it, but the fields within
the struct remain private. This is important in this case because we want to
ensure that whenever a value is added or removed from the list, the average is
also updated. We do this by implementing `add`, `remove`, and `average` methods
on the struct, as shown in Listing 18-2:

<Listing number="18-2" file-name="src/lib.rs" caption="Listing 18-2: Implementations of the public methods `add`, `remove`, and `average` on `AveragedCollection`">

<span class="filename">Filename: src/lib.rs</span>

```rust,noplayground
{{#rustdoc_include ../listings/ch18-oop/listing-18-02/src/lib.rs:here}}
```

<span class="caption">Listing 18-2: Implementations of the public methods
`add`, `remove`, and `average` on `AveragedCollection`</span>
</Listing>

The public methods `add`, `remove`, and `average` are the only ways to access
or modify data in an instance of `AveragedCollection`. When an item is added
Expand Down
40 changes: 16 additions & 24 deletions src/ch18-02-trait-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,41 +66,38 @@ behavior.
Listing 18-3 shows how to define a trait named `Draw` with one method named
`draw`:

<span class="filename">Filename: src/lib.rs</span>
<Listing number="18-3" file-name="src/lib.rs" caption="Definition of the `Draw` trait">

```rust,noplayground
{{#rustdoc_include ../listings/ch18-oop/listing-18-03/src/lib.rs}}
```

<span class="caption">Listing 18-3: Definition of the `Draw` trait</span>
</Listing>

This syntax should look familiar from our discussions on how to define traits
in Chapter 10. Next comes some new syntax: Listing 18-4 defines a struct named
`Screen` that holds a vector named `components`. This vector is of type
`Box<dyn Draw>`, which is a trait object; it’s a stand-in for any type inside
a `Box` that implements the `Draw` trait.

<span class="filename">Filename: src/lib.rs</span>
<Listing number="18-4" file-name="src/lib.rs" caption="Definition of the `Screen` struct with a `components` field holding a vector of trait objects that implement the `Draw` trait">

```rust,noplayground
{{#rustdoc_include ../listings/ch18-oop/listing-18-04/src/lib.rs:here}}
```

<span class="caption">Listing 18-4: Definition of the `Screen` struct with a
`components` field holding a vector of trait objects that implement the `Draw`
trait</span>
</Listing>

On the `Screen` struct, we’ll define a method named `run` that will call the
`draw` method on each of its `components`, as shown in Listing 18-5:

<span class="filename">Filename: src/lib.rs</span>
<Listing number="18-5" file-name="src/lib.rs" caption="A `run` method on `Screen` that calls the `draw` method on each component">

```rust,noplayground
{{#rustdoc_include ../listings/ch18-oop/listing-18-05/src/lib.rs:here}}
```

<span class="caption">Listing 18-5: A `run` method on `Screen` that calls the
`draw` method on each component</span>
</Listing>

This works differently from defining a struct that uses a generic type
parameter with trait bounds. A generic type parameter can only be substituted
Expand All @@ -109,14 +106,13 @@ concrete types to fill in for the trait object at runtime. For example, we
could have defined the `Screen` struct using a generic type and a trait bound
as in Listing 18-6:

<span class="filename">Filename: src/lib.rs</span>
<Listing number="18-6" file-name="src/lib.rs" caption="An alternate implementation of the `Screen` struct and its `run` method using generics and trait bounds">

```rust,noplayground
{{#rustdoc_include ../listings/ch18-oop/listing-18-06/src/lib.rs:here}}
```

<span class="caption">Listing 18-6: An alternate implementation of the `Screen`
struct and its `run` method using generics and trait bounds</span>
</Listing>

This restricts us to a `Screen` instance that has a list of components all of
type `Button` or all of type `TextField`. If you’ll only ever have homogeneous
Expand All @@ -136,14 +132,13 @@ of this book, so the `draw` method won’t have any useful implementation in its
body. To imagine what the implementation might look like, a `Button` struct
might have fields for `width`, `height`, and `label`, as shown in Listing 18-7:

<span class="filename">Filename: src/lib.rs</span>
<Listing number="18-7" file-name="src/lib.rs" caption="A `Button` struct that implements the `Draw` trait">

```rust,noplayground
{{#rustdoc_include ../listings/ch18-oop/listing-18-07/src/lib.rs:here}}
```

<span class="caption">Listing 18-7: A `Button` struct that implements the
`Draw` trait</span>
</Listing>

The `width`, `height`, and `label` fields on `Button` will differ from the
fields on other components; for example, a `TextField` type might have those
Expand All @@ -159,29 +154,27 @@ If someone using our library decides to implement a `SelectBox` struct that has
`width`, `height`, and `options` fields, they implement the `Draw` trait on the
`SelectBox` type as well, as shown in Listing 18-8:

<span class="filename">Filename: src/main.rs</span>
<Listing number="18-8" file-name="src/main.rs" caption="Another crate using `gui` and implementing the `Draw` trait on a `SelectBox` struct">

```rust,ignore
{{#rustdoc_include ../listings/ch18-oop/listing-18-08/src/main.rs:here}}
```

<span class="caption">Listing 18-8: Another crate using `gui` and implementing
the `Draw` trait on a `SelectBox` struct</span>
</Listing>

Our library’s user can now write their `main` function to create a `Screen`
instance. To the `Screen` instance, they can add a `SelectBox` and a `Button`
by putting each in a `Box<T>` to become a trait object. They can then call the
`run` method on the `Screen` instance, which will call `draw` on each of the
components. Listing 18-9 shows this implementation:

<span class="filename">Filename: src/main.rs</span>
<Listing number="18-9" file-name="src/main.rs" caption="Using trait objects to store values of different types that implement the same trait">

```rust,ignore
{{#rustdoc_include ../listings/ch18-oop/listing-18-09/src/main.rs:here}}
```

<span class="caption">Listing 18-9: Using trait objects to store values of
different types that implement the same trait</span>
</Listing>

When we wrote the library, we didn’t know that someone might add the
`SelectBox` type, but our `Screen` implementation was able to operate on the
Expand All @@ -208,14 +201,13 @@ our code if the values don’t implement the traits that the trait objects need.
For example, Listing 18-10 shows what happens if we try to create a `Screen`
with a `String` as a component:

<span class="filename">Filename: src/main.rs</span>
<Listing number="18-10" file-name="src/main.rs" caption="Attempting to use a type that doesn’t implement the trait object’s trait">

```rust,ignore,does_not_compile
{{#rustdoc_include ../listings/ch18-oop/listing-18-10/src/main.rs}}
```

<span class="caption">Listing 18-10: Attempting to use a type that doesn’t
implement the trait object’s trait</span>
</Listing>

We’ll get this error because `String` doesn’t implement the `Draw` trait:

Expand Down
61 changes: 25 additions & 36 deletions src/ch18-03-oo-design-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,13 @@ Listing 18-11 shows this workflow in code form: this is an example usage of the
API we’ll implement in a library crate named `blog`. This won’t compile yet
because we haven’t implemented the `blog` crate.

<span class="filename">Filename: src/main.rs</span>
<Listing number="18-11" file-name="src/main.rs" caption="Code that demonstrates the desired behavior we want our `blog` crate to have">

```rust,ignore,does_not_compile
{{#rustdoc_include ../listings/ch18-oop/listing-18-11/src/main.rs:all}}
```

<span class="caption">Listing 18-11: Code that demonstrates the desired
behavior we want our `blog` crate to have</span>
</Listing>

We want to allow the user to create a new draft blog post with `Post::new`. We
want to allow text to be added to the blog post. If we try to get the post’s
Expand Down Expand Up @@ -84,15 +83,13 @@ Then `Post` will hold a trait object of `Box<dyn State>` inside an `Option<T>`
in a private field named `state` to hold the state object. You’ll see why the
`Option<T>` is necessary in a bit.

<span class="filename">Filename: src/lib.rs</span>
<Listing number="18-12" file-name="src/lib.rs" caption="Definition of a `Post` struct and a `new` function that creates a new `Post` instance, a `State` trait, and a `Draft` struct">

```rust,noplayground
{{#rustdoc_include ../listings/ch18-oop/listing-18-12/src/lib.rs}}
```

<span class="caption">Listing 18-12: Definition of a `Post` struct and a `new`
function that creates a new `Post` instance, a `State` trait, and a `Draft`
struct</span>
</Listing>

The `State` trait defines the behavior shared by different post states. The
state objects are `Draft`, `PendingReview`, and `Published`, and they will all
Expand All @@ -117,14 +114,13 @@ the `content` field’s data is read. The `add_text` method is pretty
straightforward, so let’s add the implementation in Listing 18-13 to the `impl
Post` block:

<span class="filename">Filename: src/lib.rs</span>
<Listing number="18-13" fie-name="src/lib.rs" caption="Implementing the `add_text` method to add text to a post’s `content`">

```rust,noplayground
{{#rustdoc_include ../listings/ch18-oop/listing-18-13/src/lib.rs:here}}
```

<span class="caption">Listing 18-13: Implementing the `add_text` method to add
text to a post’s `content`</span>
</Listing>

The `add_text` method takes a mutable reference to `self`, because we’re
changing the `Post` instance that we’re calling `add_text` on. We then call
Expand All @@ -145,14 +141,13 @@ once we implement the ability to change a post’s state so it can be published.
So far, posts can only be in the draft state, so the post content should always
be empty. Listing 18-14 shows this placeholder implementation:

<span class="filename">Filename: src/lib.rs</span>
<Listing number="18-14" file-name="src/lib.rs" caption="Adding a placeholder implementation for the `content` method on `Post` that always returns an empty string slice">

```rust,noplayground
{{#rustdoc_include ../listings/ch18-oop/listing-18-14/src/lib.rs:here}}
```

<span class="caption">Listing 18-14: Adding a placeholder implementation for
the `content` method on `Post` that always returns an empty string slice</span>
</Listing>

With this added `content` method, everything in Listing 18-11 up to line 7
works as intended.
Expand All @@ -162,14 +157,13 @@ works as intended.
Next, we need to add functionality to request a review of a post, which should
change its state from `Draft` to `PendingReview`. Listing 18-15 shows this code:

<span class="filename">Filename: src/lib.rs</span>
<Listing number="18-15" file-name="src/lib.rs" caption="Implementing `request_review` methods on `Post` and the `State` trait">

```rust,noplayground
{{#rustdoc_include ../listings/ch18-oop/listing-18-15/src/lib.rs:here}}
```

<span class="caption">Listing 18-15: Implementing `request_review` methods on
`Post` and the `State` trait</span>
</Listing>

We give `Post` a public method named `request_review` that will take a mutable
reference to `self`. Then we call an internal `request_review` method on the
Expand Down Expand Up @@ -222,14 +216,13 @@ The `approve` method will be similar to the `request_review` method: it will
set `state` to the value that the current state says it should have when that
state is approved, as shown in Listing 18-16:

<span class="filename">Filename: src/lib.rs</span>
<Listing number="18-16" file-name="src/lib.rs" caption="Implementing the `approve` method on `Post` and the `State` trait">

```rust,noplayground
{{#rustdoc_include ../listings/ch18-oop/listing-18-16/src/lib.rs:here}}
```

<span class="caption">Listing 18-16: Implementing the `approve` method on
`Post` and the `State` trait</span>
</Listing>

We add the `approve` method to the `State` trait and add a new struct that
implements `State`, the `Published` state.
Expand All @@ -247,14 +240,13 @@ returned from `content` to depend on the current state of the `Post`, so we’re
going to have the `Post` delegate to a `content` method defined on its `state`,
as shown in Listing 18-17:

<span class="filename">Filename: src/lib.rs</span>
<Listing number="18-17" file-name="src/lib.rs" caption="Updating the `content` method on `Post` to delegate to a `content` method on `State`">

```rust,ignore,does_not_compile
{{#rustdoc_include ../listings/ch18-oop/listing-18-17/src/lib.rs:here}}
```

<span class="caption">Listing 18-17: Updating the `content` method on `Post` to
delegate to a `content` method on `State`</span>
</Listing>

Because the goal is to keep all these rules inside the structs that implement
`State`, we call a `content` method on the value in `state` and pass the post
Expand Down Expand Up @@ -282,14 +274,13 @@ we need to add `content` to the `State` trait definition, and that is where
we’ll put the logic for what content to return depending on which state we
have, as shown in Listing 18-18:

<span class="filename">Filename: src/lib.rs</span>
<Listing number="18-18" file-name="src/lib.rs" caption="Adding the `content` method to the `State` trait">

```rust,noplayground
{{#rustdoc_include ../listings/ch18-oop/listing-18-18/src/lib.rs:here}}
```

<span class="caption">Listing 18-18: Adding the `content` method to the `State`
trait</span>
</Listing>

We add a default implementation for the `content` method that returns an empty
string slice. That means we don’t need to implement `content` on the `Draft`
Expand Down Expand Up @@ -383,12 +374,14 @@ draft posts where only published posts are allowed by issuing a compiler error.

Let’s consider the first part of `main` in Listing 18-11:

<span class="filename">Filename: src/main.rs</span>
<Listing file-name="src/main.rs">

```rust,ignore
{{#rustdoc_include ../listings/ch18-oop/listing-18-11/src/main.rs:here}}
```

</Listing>

We still enable the creation of new posts in the draft state using `Post::new`
and the ability to add text to the post’s content. But instead of having a
`content` method on a draft post that returns an empty string, we’ll make it so
Expand All @@ -399,14 +392,13 @@ display draft post content in production, because that code won’t even compile
Listing 18-19 shows the definition of a `Post` struct and a `DraftPost` struct,
as well as methods on each:

<span class="filename">Filename: src/lib.rs</span>
<Listing number="18-19" file-name="src/lib.rs" caption="A `Post` with a `content` method and `DraftPost` without a `content` method">

```rust,noplayground
{{#rustdoc_include ../listings/ch18-oop/listing-18-19/src/lib.rs}}
```

<span class="caption">Listing 18-19: A `Post` with a `content` method and a
`DraftPost` without a `content` method</span>
</Listing>

Both the `Post` and `DraftPost` structs have a private `content` field that
stores the blog post text. The structs no longer have the `state` field because
Expand Down Expand Up @@ -435,15 +427,13 @@ these constraints by adding another struct, `PendingReviewPost`, defining the
defining an `approve` method on `PendingReviewPost` to return a `Post`, as
shown in Listing 18-20:

<span class="filename">Filename: src/lib.rs</span>
<Listing number="18-20" file-name="src/lib.rs" caption="A `PendingReviewPost` that gets created by calling `request_review` on `DraftPost` and an `approve` method that turns a `PendingReviewPost` into a published `Post`">

```rust,noplayground
{{#rustdoc_include ../listings/ch18-oop/listing-18-20/src/lib.rs:here}}
```

<span class="caption">Listing 18-20: A `PendingReviewPost` that gets created by
calling `request_review` on `DraftPost` and an `approve` method that turns a
`PendingReviewPost` into a published `Post`</span>
</Listing>

The `request_review` and `approve` methods take ownership of `self`, thus
consuming the `DraftPost` and `PendingReviewPost` instances and transforming
Expand All @@ -465,14 +455,13 @@ pending review posts’ contents be empty strings, nor do we need them: we can
compile code that tries to use the content of posts in those states any longer.
The updated code in `main` is shown in Listing 18-21:

<span class="filename">Filename: src/main.rs</span>
<Listing number="18-21" file-name="src/main.rs" caption="Modifications to `main` to use the new implementation of the blog post workflow">

```rust,ignore
{{#rustdoc_include ../listings/ch18-oop/listing-18-21/src/main.rs}}
```

<span class="caption">Listing 18-21: Modifications to `main` to use the new
implementation of the blog post workflow</span>
</Listing>

The changes we needed to make to `main` to reassign `post` mean that this
implementation doesn’t quite follow the object-oriented state pattern anymore:
Expand Down
Loading