Skip to content

Commit

Permalink
Merge pull request #4060 from rust-lang/listing-for-final-chs
Browse files Browse the repository at this point in the history
Use `<Listing>` for final chapters (after restructuring)
  • Loading branch information
chriskrycho authored Oct 9, 2024
2 parents 68b4d35 + 7ce0849 commit 9e79bd0
Show file tree
Hide file tree
Showing 14 changed files with 293 additions and 307 deletions.
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

0 comments on commit 9e79bd0

Please sign in to comment.