feat: added cleanscript

This commit is contained in:
2026-06-22 21:27:36 +05:30
parent dbddc0ce2d
commit 4581eea409
309 changed files with 14551 additions and 46035 deletions

23
ch18/ch18-00-oop.html Normal file
View File

@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Object Oriented Programming Features</title>
</head>
<body>
<h1 id="object-oriented-programming-features"><a class="header" href="#object-oriented-programming-features">Object-Oriented Programming Features</a></h1>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="object-oriented-programming-features-of-rust"></a></p>
<p>Object-oriented programming (OOP) is a way of modeling programs. Objects as a
programmatic concept were introduced in the programming language Simula in the
1960s. Those objects influenced Alan Kays programming architecture in which
objects pass messages to each other. To describe this architecture, he coined
the term <em>object-oriented programming</em> in 1967. Many competing definitions
describe what OOP is, and by some of these definitions Rust is object oriented
but by others it is not. In this chapter, well explore certain characteristics
that are commonly considered object oriented and how those characteristics
translate to idiomatic Rust. Well then show you how to implement an
object-oriented design pattern in Rust and discuss the trade-offs of doing so
versus implementing a solution using some of Rusts strengths instead.</p>
</body>
</html>

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to... <a href="../ch19/ch19-00-patterns.html">ch19-00-patterns.html</a>.</p>
</body>
</html>

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to... <a href="../ch19/ch19-01-all-the-places-for-patterns.html">ch19-01-all-the-places-for-patterns.html</a>.</p>
</body>
</html>

View File

@@ -0,0 +1,165 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Characteristics of Object-Oriented Languages</title>
</head>
<body>
<h2 id="characteristics-of-object-oriented-languages"><a class="header" href="#characteristics-of-object-oriented-languages">Characteristics of Object-Oriented Languages</a></h2>
<p>There is no consensus in the programming community about what features a
language must have to be considered object oriented. Rust is influenced by many
programming paradigms, including OOP; for example, we explored the features
that came from functional programming in Chapter 13. Arguably, OOP languages
share certain common characteristics—namely, objects, encapsulation, and
inheritance. Lets look at what each of those characteristics means and whether
Rust supports it.</p>
<h3 id="objects-contain-data-and-behavior"><a class="header" href="#objects-contain-data-and-behavior">Objects Contain Data and Behavior</a></h3>
<p>The book <em>Design Patterns: Elements of Reusable Object-Oriented Software</em> by
Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Addison-Wesley,
1994), colloquially referred to as <em>The Gang of Four</em> book, is a catalog of
object-oriented design patterns. It defines OOP in this way:</p>
<blockquote>
<p>Object-oriented programs are made up of objects. An <strong>object</strong> packages both
data and the procedures that operate on that data. The procedures are
typically called <strong>methods</strong> or <strong>operations</strong>.</p>
</blockquote>
<p>Using this definition, Rust is object oriented: Structs and enums have data,
and <code>impl</code> blocks provide methods on structs and enums. Even though structs and
enums with methods arent <em>called</em> objects, they provide the same
functionality, according to the Gang of Fours definition of objects.</p>
<h3 id="encapsulation-that-hides-implementation-details"><a class="header" href="#encapsulation-that-hides-implementation-details">Encapsulation That Hides Implementation Details</a></h3>
<p>Another aspect commonly associated with OOP is the idea of <em>encapsulation</em>,
which means that the implementation details of an object arent accessible to
code using that object. Therefore, the only way to interact with an object is
through its public API; code using the object shouldnt be able to reach into
the objects internals and change data or behavior directly. This enables the
programmer to change and refactor an objects internals without needing to
change the code that uses the object.</p>
<p>We discussed how to control encapsulation in Chapter 7: We can use the <code>pub</code>
keyword to decide which modules, types, functions, and methods in our code
should be public, and by default everything else is private. For example, we
can define a struct <code>AveragedCollection</code> that has a field containing a vector
of <code>i32</code> values. The struct can also have a field that contains the average of
the values in the vector, meaning the average doesnt have to be computed on
demand whenever anyone needs it. In other words, <code>AveragedCollection</code> will
cache the calculated average for us. Listing 18-1 has the definition of the
<code>AveragedCollection</code> struct.</p>
<figure class="listing" id="listing-18-1">
<span class="file-name">Filename: src/lib.rs</span>
<pre><code class="language-rust noplayground">pub struct AveragedCollection {
list: Vec&lt;i32&gt;,
average: f64,
}</code></pre>
<figcaption><a href="#listing-18-1">Listing 18-1</a>: An <code>AveragedCollection</code> struct that maintains a list of integers and the average of the items in the collection</figcaption>
</figure>
<p>The struct is marked <code>pub</code> 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 <code>add</code>, <code>remove</code>, and <code>average</code> methods
on the struct, as shown in Listing 18-2.</p>
<figure class="listing" id="listing-18-2">
<span class="file-name">Filename: src/lib.rs</span>
<pre><code class="language-rust noplayground"><span class="boring">pub struct AveragedCollection {
</span><span class="boring"> list: Vec&lt;i32&gt;,
</span><span class="boring"> average: f64,
</span><span class="boring">}
</span><span class="boring">
</span>impl AveragedCollection {
pub fn add(&amp;mut self, value: i32) {
self.list.push(value);
self.update_average();
}
pub fn remove(&amp;mut self) -&gt; Option&lt;i32&gt; {
let result = self.list.pop();
match result {
Some(value) =&gt; {
self.update_average();
Some(value)
}
None =&gt; None,
}
}
pub fn average(&amp;self) -&gt; f64 {
self.average
}
fn update_average(&amp;mut self) {
let total: i32 = self.list.iter().sum();
self.average = total as f64 / self.list.len() as f64;
}
}</code></pre>
<figcaption><a href="#listing-18-2">Listing 18-2</a>: Implementations of the public methods <code>add</code>, <code>remove</code>, and <code>average</code> on <code>AveragedCollection</code></figcaption>
</figure>
<p>The public methods <code>add</code>, <code>remove</code>, and <code>average</code> are the only ways to access
or modify data in an instance of <code>AveragedCollection</code>. When an item is added to
<code>list</code> using the <code>add</code> method or removed using the <code>remove</code> method, the
implementations of each call the private <code>update_average</code> method that handles
updating the <code>average</code> field as well.</p>
<p>We leave the <code>list</code> and <code>average</code> fields private so that there is no way for
external code to add or remove items to or from the <code>list</code> field directly;
otherwise, the <code>average</code> field might become out of sync when the <code>list</code>
changes. The <code>average</code> method returns the value in the <code>average</code> field,
allowing external code to read the <code>average</code> but not modify it.</p>
<p>Because weve encapsulated the implementation details of the struct
<code>AveragedCollection</code>, we can easily change aspects, such as the data structure,
in the future. For instance, we could use a <code>HashSet&lt;i32&gt;</code> instead of a
<code>Vec&lt;i32&gt;</code> for the <code>list</code> field. As long as the signatures of the <code>add</code>,
<code>remove</code>, and <code>average</code> public methods stayed the same, code using
<code>AveragedCollection</code> wouldnt need to change. If we made <code>list</code> public instead,
this wouldnt necessarily be the case: <code>HashSet&lt;i32&gt;</code> and <code>Vec&lt;i32&gt;</code> have
different methods for adding and removing items, so the external code would
likely have to change if it were modifying <code>list</code> directly.</p>
<p>If encapsulation is a required aspect for a language to be considered object
oriented, then Rust meets that requirement. The option to use <code>pub</code> or not for
different parts of code enables encapsulation of implementation details.</p>
<h3 id="inheritance-as-a-type-system-and-as-code-sharing"><a class="header" href="#inheritance-as-a-type-system-and-as-code-sharing">Inheritance as a Type System and as Code Sharing</a></h3>
<p><em>Inheritance</em> is a mechanism whereby an object can inherit elements from
another objects definition, thus gaining the parent objects data and behavior
without you having to define them again.</p>
<p>If a language must have inheritance to be object oriented, then Rust is not
such a language. There is no way to define a struct that inherits the parent
structs fields and method implementations without using a macro.</p>
<p>However, if youre used to having inheritance in your programming toolbox, you
can use other solutions in Rust, depending on your reason for reaching for
inheritance in the first place.</p>
<p>You would choose inheritance for two main reasons. One is for reuse of code:
You can implement particular behavior for one type, and inheritance enables you
to reuse that implementation for a different type. You can do this in a limited
way in Rust code using default trait method implementations, which you saw in
Listing 10-14 when we added a default implementation of the <code>summarize</code> method
on the <code>Summary</code> trait. Any type implementing the <code>Summary</code> trait would have
the <code>summarize</code> method available on it without any further code. This is
similar to a parent class having an implementation of a method and an
inheriting child class also having the implementation of the method. We can
also override the default implementation of the <code>summarize</code> method when we
implement the <code>Summary</code> trait, which is similar to a child class overriding the
implementation of a method inherited from a parent class.</p>
<p>The other reason to use inheritance relates to the type system: to enable a
child type to be used in the same places as the parent type. This is also
called <em>polymorphism</em>, which means that you can substitute multiple objects for
each other at runtime if they share certain characteristics.</p>
<section class="note" aria-role="note">
<h3 id="polymorphism"><a class="header" href="#polymorphism">Polymorphism</a></h3>
<p>To many people, polymorphism is synonymous with inheritance. But its
actually a more general concept that refers to code that can work with data of
multiple types. For inheritance, those types are generally subclasses.</p>
<p>Rust instead uses generics to abstract over different possible types and
trait bounds to impose constraints on what those types must provide. This is
sometimes called <em>bounded parametric polymorphism</em>.</p>
</section>
<p>Rust has chosen a different set of trade-offs by not offering inheritance.
Inheritance is often at risk of sharing more code than necessary. Subclasses
shouldnt always share all characteristics of their parent class but will do so
with inheritance. This can make a programs design less flexible. It also
introduces the possibility of calling methods on subclasses that dont make
sense or that cause errors because the methods dont apply to the subclass. In
addition, some languages will only allow <em>single inheritance</em> (meaning a
subclass can only inherit from one class), further restricting the flexibility
of a programs design.</p>
<p>For these reasons, Rust takes the different approach of using trait objects
instead of inheritance to achieve polymorphism at runtime. Lets look at how
trait objects work.</p>
</body>
</html>

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to... <a href="../ch19/ch19-02-refutability.html">ch19-02-refutability.html</a>.</p>
</body>
</html>

View File

@@ -0,0 +1,339 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Using Trait Objects to Abstract over Shared Behavior</title>
</head>
<body>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="using-trait-objects-that-allow-for-values-of-different-types"></a></p>
<h2 id="using-trait-objects-to-abstract-over-shared-behavior"><a class="header" href="#using-trait-objects-to-abstract-over-shared-behavior">Using Trait Objects to Abstract over Shared Behavior</a></h2>
<p>In Chapter 8, we mentioned that one limitation of vectors is that they can
store elements of only one type. We created a workaround in Listing 8-9 where
we defined a <code>SpreadsheetCell</code> enum that had variants to hold integers, floats,
and text. This meant we could store different types of data in each cell and
still have a vector that represented a row of cells. This is a perfectly good
solution when our interchangeable items are a fixed set of types that we know
when our code is compiled.</p>
<p>However, sometimes we want our library user to be able to extend the set of
types that are valid in a particular situation. To show how we might achieve
this, well create an example graphical user interface (GUI) tool that iterates
through a list of items, calling a <code>draw</code> method on each one to draw it to the
screen—a common technique for GUI tools. Well create a library crate called
<code>gui</code> that contains the structure of a GUI library. This crate might include
some types for people to use, such as <code>Button</code> or <code>TextField</code>. In addition,
<code>gui</code> users will want to create their own types that can be drawn: For
instance, one programmer might add an <code>Image</code>, and another might add a
<code>SelectBox</code>.</p>
<p>At the time of writing the library, we cant know and define all the types
other programmers might want to create. But we do know that <code>gui</code> needs to keep
track of many values of different types, and it needs to call a <code>draw</code> method
on each of these differently typed values. It doesnt need to know exactly what
will happen when we call the <code>draw</code> method, just that the value will have that
method available for us to call.</p>
<p>To do this in a language with inheritance, we might define a class named
<code>Component</code> that has a method named <code>draw</code> on it. The other classes, such as
<code>Button</code>, <code>Image</code>, and <code>SelectBox</code>, would inherit from <code>Component</code> and thus
inherit the <code>draw</code> method. They could each override the <code>draw</code> method to define
their custom behavior, but the framework could treat all of the types as if
they were <code>Component</code> instances and call <code>draw</code> on them. But because Rust
doesnt have inheritance, we need another way to structure the <code>gui</code> library to
allow users to create new types compatible with the library.</p>
<h3 id="defining-a-trait-for-common-behavior"><a class="header" href="#defining-a-trait-for-common-behavior">Defining a Trait for Common Behavior</a></h3>
<p>To implement the behavior that we want <code>gui</code> to have, well define a trait
named <code>Draw</code> that will have one method named <code>draw</code>. Then, we can define a
vector that takes a trait object. A <em>trait object</em> points to both an instance
of a type implementing our specified trait and a table used to look up trait
methods on that type at runtime. We create a trait object by specifying some
sort of pointer, such as a reference or a <code>Box&lt;T&gt;</code> smart pointer, then the
<code>dyn</code> keyword, and then specifying the relevant trait. (Well talk about the
reason trait objects must use a pointer in <a href="../ch20/ch20-03-advanced-types.html#dynamically-sized-types-and-the-sized-trait">“Dynamically Sized Types and the
<code>Sized</code> Trait”</a><!-- ignore --> in Chapter 20.) We can use
trait objects in place of a generic or concrete type. Wherever we use a trait
object, Rusts type system will ensure at compile time that any value used in
that context will implement the trait objects trait. Consequently, we dont
need to know all the possible types at compile time.</p>
<p>Weve mentioned that, in Rust, we refrain from calling structs and enums
“objects” to distinguish them from other languages objects. In a struct or
enum, the data in the struct fields and the behavior in <code>impl</code> blocks are
separated, whereas in other languages, the data and behavior combined into one
concept is often labeled an object. Trait objects differ from objects in other
languages in that we cant add data to a trait object. Trait objects arent as
generally useful as objects in other languages: Their specific purpose is to
allow abstraction across common behavior.</p>
<p>Listing 18-3 shows how to define a trait named <code>Draw</code> with one method named
<code>draw</code>.</p>
<figure class="listing" id="listing-18-3">
<span class="file-name">Filename: src/lib.rs</span>
<pre><code class="language-rust noplayground">pub trait Draw {
fn draw(&amp;self);
}</code></pre>
<figcaption><a href="#listing-18-3">Listing 18-3</a>: Definition of the <code>Draw</code> trait</figcaption>
</figure>
<p>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
<code>Screen</code> that holds a vector named <code>components</code>. This vector is of type
<code>Box&lt;dyn Draw&gt;</code>, which is a trait object; its a stand-in for any type inside a
<code>Box</code> that implements the <code>Draw</code> trait.</p>
<figure class="listing" id="listing-18-4">
<span class="file-name">Filename: src/lib.rs</span>
<pre><code class="language-rust noplayground"><span class="boring">pub trait Draw {
</span><span class="boring"> fn draw(&amp;self);
</span><span class="boring">}
</span><span class="boring">
</span>pub struct Screen {
pub components: Vec&lt;Box&lt;dyn Draw&gt;&gt;,
}</code></pre>
<figcaption><a href="#listing-18-4">Listing 18-4</a>: Definition of the <code>Screen</code> struct with a <code>components</code> field holding a vector of trait objects that implement the <code>Draw</code> trait</figcaption>
</figure>
<p>On the <code>Screen</code> struct, well define a method named <code>run</code> that will call the
<code>draw</code> method on each of its <code>components</code>, as shown in Listing 18-5.</p>
<figure class="listing" id="listing-18-5">
<span class="file-name">Filename: src/lib.rs</span>
<pre><code class="language-rust noplayground"><span class="boring">pub trait Draw {
</span><span class="boring"> fn draw(&amp;self);
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub struct Screen {
</span><span class="boring"> pub components: Vec&lt;Box&lt;dyn Draw&gt;&gt;,
</span><span class="boring">}
</span><span class="boring">
</span>impl Screen {
pub fn run(&amp;self) {
for component in self.components.iter() {
component.draw();
}
}
}</code></pre>
<figcaption><a href="#listing-18-5">Listing 18-5</a>: A <code>run</code> method on <code>Screen</code> that calls the <code>draw</code> method on each component</figcaption>
</figure>
<p>This works differently from defining a struct that uses a generic type
parameter with trait bounds. A generic type parameter can be substituted with
only one concrete type at a time, whereas trait objects allow for multiple
concrete types to fill in for the trait object at runtime. For example, we
could have defined the <code>Screen</code> struct using a generic type and a trait bound,
as in Listing 18-6.</p>
<figure class="listing" id="listing-18-6">
<span class="file-name">Filename: src/lib.rs</span>
<pre><code class="language-rust noplayground"><span class="boring">pub trait Draw {
</span><span class="boring"> fn draw(&amp;self);
</span><span class="boring">}
</span><span class="boring">
</span>pub struct Screen&lt;T: Draw&gt; {
pub components: Vec&lt;T&gt;,
}
impl&lt;T&gt; Screen&lt;T&gt;
where
T: Draw,
{
pub fn run(&amp;self) {
for component in self.components.iter() {
component.draw();
}
}
}</code></pre>
<figcaption><a href="#listing-18-6">Listing 18-6</a>: An alternate implementation of the <code>Screen</code> struct and its <code>run</code> method using generics and trait bounds</figcaption>
</figure>
<p>This restricts us to a <code>Screen</code> instance that has a list of components all of
type <code>Button</code> or all of type <code>TextField</code>. If youll only ever have homogeneous
collections, using generics and trait bounds is preferable because the
definitions will be monomorphized at compile time to use the concrete types.</p>
<p>On the other hand, with the method using trait objects, one <code>Screen</code> instance
can hold a <code>Vec&lt;T&gt;</code> that contains a <code>Box&lt;Button&gt;</code> as well as a
<code>Box&lt;TextField&gt;</code>. Lets look at how this works, and then well talk about the
runtime performance implications.</p>
<h3 id="implementing-the-trait"><a class="header" href="#implementing-the-trait">Implementing the Trait</a></h3>
<p>Now well add some types that implement the <code>Draw</code> trait. Well provide the
<code>Button</code> type. Again, actually implementing a GUI library is beyond the scope
of this book, so the <code>draw</code> method wont have any useful implementation in its
body. To imagine what the implementation might look like, a <code>Button</code> struct
might have fields for <code>width</code>, <code>height</code>, and <code>label</code>, as shown in Listing 18-7.</p>
<figure class="listing" id="listing-18-7">
<span class="file-name">Filename: src/lib.rs</span>
<pre><code class="language-rust noplayground"><span class="boring">pub trait Draw {
</span><span class="boring"> fn draw(&amp;self);
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub struct Screen {
</span><span class="boring"> pub components: Vec&lt;Box&lt;dyn Draw&gt;&gt;,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Screen {
</span><span class="boring"> pub fn run(&amp;self) {
</span><span class="boring"> for component in self.components.iter() {
</span><span class="boring"> component.draw();
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span>pub struct Button {
pub width: u32,
pub height: u32,
pub label: String,
}
impl Draw for Button {
fn draw(&amp;self) {
// code to actually draw a button
}
}</code></pre>
<figcaption><a href="#listing-18-7">Listing 18-7</a>: A <code>Button</code> struct that implements the <code>Draw</code> trait</figcaption>
</figure>
<p>The <code>width</code>, <code>height</code>, and <code>label</code> fields on <code>Button</code> will differ from the
fields on other components; for example, a <code>TextField</code> type might have those
same fields plus a <code>placeholder</code> field. Each of the types we want to draw on
the screen will implement the <code>Draw</code> trait but will use different code in the
<code>draw</code> method to define how to draw that particular type, as <code>Button</code> has here
(without the actual GUI code, as mentioned). The <code>Button</code> type, for instance,
might have an additional <code>impl</code> block containing methods related to what
happens when a user clicks the button. These kinds of methods wont apply to
types like <code>TextField</code>.</p>
<p>If someone using our library decides to implement a <code>SelectBox</code> struct that has
<code>width</code>, <code>height</code>, and <code>options</code> fields, they would implement the <code>Draw</code> trait
on the <code>SelectBox</code> type as well, as shown in Listing 18-8.</p>
<figure class="listing" id="listing-18-8">
<span class="file-name">Filename: src/main.rs</span>
<pre><code class="language-rust ignore">use gui::Draw;
struct SelectBox {
width: u32,
height: u32,
options: Vec&lt;String&gt;,
}
impl Draw for SelectBox {
fn draw(&amp;self) {
// code to actually draw a select box
}
}
<span class="boring">
</span><span class="boring">fn main() {}</span></code></pre>
<figcaption><a href="#listing-18-8">Listing 18-8</a>: Another crate using <code>gui</code> and implementing the <code>Draw</code> trait on a <code>SelectBox</code> struct</figcaption>
</figure>
<p>Our librarys user can now write their <code>main</code> function to create a <code>Screen</code>
instance. To the <code>Screen</code> instance, they can add a <code>SelectBox</code> and a <code>Button</code>
by putting each in a <code>Box&lt;T&gt;</code> to become a trait object. They can then call the
<code>run</code> method on the <code>Screen</code> instance, which will call <code>draw</code> on each of the
components. Listing 18-9 shows this implementation.</p>
<figure class="listing" id="listing-18-9">
<span class="file-name">Filename: src/main.rs</span>
<pre><code class="language-rust ignore"><span class="boring">use gui::Draw;
</span><span class="boring">
</span><span class="boring">struct SelectBox {
</span><span class="boring"> width: u32,
</span><span class="boring"> height: u32,
</span><span class="boring"> options: Vec&lt;String&gt;,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Draw for SelectBox {
</span><span class="boring"> fn draw(&amp;self) {
</span><span class="boring"> // code to actually draw a select box
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span>use gui::{Button, Screen};
fn main() {
let screen = Screen {
components: vec![
Box::new(SelectBox {
width: 75,
height: 10,
options: vec![
String::from("Yes"),
String::from("Maybe"),
String::from("No"),
],
}),
Box::new(Button {
width: 50,
height: 10,
label: String::from("OK"),
}),
],
};
screen.run();
}</code></pre>
<figcaption><a href="#listing-18-9">Listing 18-9</a>: Using trait objects to store values of different types that implement the same trait</figcaption>
</figure>
<p>When we wrote the library, we didnt know that someone might add the
<code>SelectBox</code> type, but our <code>Screen</code> implementation was able to operate on the
new type and draw it because <code>SelectBox</code> implements the <code>Draw</code> trait, which
means it implements the <code>draw</code> method.</p>
<p>This concept—of being concerned only with the messages a value responds to
rather than the values concrete type—is similar to the concept of <em>duck
typing</em> in dynamically typed languages: If it walks like a duck and quacks like
a duck, then it must be a duck! In the implementation of <code>run</code> on <code>Screen</code> in
Listing 18-5, <code>run</code> doesnt need to know what the concrete type of each
component is. It doesnt check whether a component is an instance of a <code>Button</code>
or a <code>SelectBox</code>, it just calls the <code>draw</code> method on the component. By
specifying <code>Box&lt;dyn Draw&gt;</code> as the type of the values in the <code>components</code>
vector, weve defined <code>Screen</code> to need values that we can call the <code>draw</code>
method on.</p>
<p>The advantage of using trait objects and Rusts type system to write code
similar to code using duck typing is that we never have to check whether a
value implements a particular method at runtime or worry about getting errors
if a value doesnt implement a method but we call it anyway. Rust wont compile
our code if the values dont implement the traits that the trait objects need.</p>
<p>For example, Listing 18-10 shows what happens if we try to create a <code>Screen</code>
with a <code>String</code> as a component.</p>
<figure class="listing" id="listing-18-10">
<span class="file-name">Filename: src/main.rs</span>
<pre><code class="language-rust ignore does_not_compile">use gui::Screen;
fn main() {
let screen = Screen {
components: vec![Box::new(String::from("Hi"))],
};
screen.run();
}</code></pre>
<figcaption><a href="#listing-18-10">Listing 18-10</a>: Attempting to use a type that doesnt implement the trait objects trait</figcaption>
</figure>
<p>Well get this error because <code>String</code> doesnt implement the <code>Draw</code> trait:</p>
<pre><code class="language-console">$ cargo run
Compiling gui v0.1.0 (file:///projects/gui)
error[E0277]: the trait bound `String: Draw` is not satisfied
--&gt; src/main.rs:5:26
|
5 | components: vec![Box::new(String::from("Hi"))],
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Draw` is not implemented for `String`
|
= help: the trait `Draw` is implemented for `Button`
= note: required for the cast from `Box&lt;String&gt;` to `Box&lt;dyn Draw&gt;`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `gui` (bin "gui") due to 1 previous error
</code></pre>
<p>This error lets us know that either were passing something to <code>Screen</code> that we
didnt mean to pass and so should pass a different type, or we should implement
<code>Draw</code> on <code>String</code> so that <code>Screen</code> is able to call <code>draw</code> on it.</p>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="trait-objects-perform-dynamic-dispatch"></a></p>
<h3 id="performing-dynamic-dispatch"><a class="header" href="#performing-dynamic-dispatch">Performing Dynamic Dispatch</a></h3>
<p>Recall in <a href="../ch10/ch10-01-syntax.html#performance-of-code-using-generics">“Performance of Code Using
Generics”</a><!-- ignore --> in Chapter 10 our
discussion on the monomorphization process performed on generics by the
compiler: The compiler generates nongeneric implementations of functions and
methods for each concrete type that we use in place of a generic type
parameter. The code that results from monomorphization is doing <em>static
dispatch</em>, which is when the compiler knows what method youre calling at
compile time. This is opposed to <em>dynamic dispatch</em>, which is when the compiler
cant tell at compile time which method youre calling. In dynamic dispatch
cases, the compiler emits code that at runtime will know which method to call.</p>
<p>When we use trait objects, Rust must use dynamic dispatch. The compiler doesnt
know all the types that might be used with the code thats using trait objects,
so it doesnt know which method implemented on which type to call. Instead, at
runtime, Rust uses the pointers inside the trait object to know which method to
call. This lookup incurs a runtime cost that doesnt occur with static dispatch.
Dynamic dispatch also prevents the compiler from choosing to inline a methods
code, which in turn prevents some optimizations, and Rust has some rules about
where you can and cannot use dynamic dispatch, called <em>dyn compatibility</em>. Those
rules are beyond the scope of this discussion, but you can read more about them
<a href="https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility">in the reference</a><!-- ignore -->. However, we did get extra
flexibility in the code that we wrote in Listing 18-5 and were able to support
in Listing 18-9, so its a trade-off to consider.</p>
</body>
</html>

View File

@@ -0,0 +1,888 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Implementing an Object-Oriented Design Pattern</title>
</head>
<body>
<h2 id="implementing-an-object-oriented-design-pattern"><a class="header" href="#implementing-an-object-oriented-design-pattern">Implementing an Object-Oriented Design Pattern</a></h2>
<p>The <em>state pattern</em> is an object-oriented design pattern. The crux of the
pattern is that we define a set of states a value can have internally. The
states are represented by a set of <em>state objects</em>, and the values behavior
changes based on its state. Were going to work through an example of a blog
post struct that has a field to hold its state, which will be a state object
from the set “draft,” “review,” or “published.”</p>
<p>The state objects share functionality: In Rust, of course, we use structs and
traits rather than objects and inheritance. Each state object is responsible
for its own behavior and for governing when it should change into another
state. The value that holds a state object knows nothing about the different
behavior of the states or when to transition between states.</p>
<p>The advantage of using the state pattern is that, when the business
requirements of the program change, we wont need to change the code of the
value holding the state or the code that uses the value. Well only need to
update the code inside one of the state objects to change its rules or perhaps
add more state objects.</p>
<p>First, were going to implement the state pattern in a more traditional
object-oriented way. Then, well use an approach thats a bit more natural in
Rust. Lets dig in to incrementally implement a blog post workflow using the
state pattern.</p>
<p>The final functionality will look like this:</p>
<ol>
<li>A blog post starts as an empty draft.</li>
<li>When the draft is done, a review of the post is requested.</li>
<li>When the post is approved, it gets published.</li>
<li>Only published blog posts return content to print so that unapproved posts
cant accidentally be published.</li>
</ol>
<p>Any other changes attempted on a post should have no effect. For example, if we
try to approve a draft blog post before weve requested a review, the post
should remain an unpublished draft.</p>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="a-traditional-object-oriented-attempt"></a></p>
<h3 id="attempting-traditional-object-oriented-style"><a class="header" href="#attempting-traditional-object-oriented-style">Attempting Traditional Object-Oriented Style</a></h3>
<p>There are infinite ways to structure code to solve the same problem, each with
different trade-offs. This sections implementation is more of a traditional
object-oriented style, which is possible to write in Rust, but doesnt take
advantage of some of Rusts strengths. Later, well demonstrate a different
solution that still uses the object-oriented design pattern but is structured
in a way that might look less familiar to programmers with object-oriented
experience. Well compare the two solutions to experience the trade-offs of
designing Rust code differently than code in other languages.</p>
<p>Listing 18-11 shows this workflow in code form: This is an example usage of the
API well implement in a library crate named <code>blog</code>. This wont compile yet
because we havent implemented the <code>blog</code> crate.</p>
<figure class="listing" id="listing-18-11">
<span class="file-name">Filename: src/main.rs</span>
<pre><code class="language-rust ignore does_not_compile">use blog::Post;
fn main() {
let mut post = Post::new();
post.add_text("I ate a salad for lunch today");
assert_eq!("", post.content());
post.request_review();
assert_eq!("", post.content());
post.approve();
assert_eq!("I ate a salad for lunch today", post.content());
}</code></pre>
<figcaption><a href="#listing-18-11">Listing 18-11</a>: Code that demonstrates the desired behavior we want our <code>blog</code> crate to have</figcaption>
</figure>
<p>We want to allow the user to create a new draft blog post with <code>Post::new</code>. We
want to allow text to be added to the blog post. If we try to get the posts
content immediately, before approval, we shouldnt get any text because the
post is still a draft. Weve added <code>assert_eq!</code> in the code for demonstration
purposes. An excellent unit test for this would be to assert that a draft blog
post returns an empty string from the <code>content</code> method, but were not going to
write tests for this example.</p>
<p>Next, we want to enable a request for a review of the post, and we want
<code>content</code> to return an empty string while waiting for the review. When the post
receives approval, it should get published, meaning the text of the post will
be returned when <code>content</code> is called.</p>
<p>Notice that the only type were interacting with from the crate is the <code>Post</code>
type. This type will use the state pattern and will hold a value that will be
one of three state objects representing the various states a post can be
in—draft, review, or published. Changing from one state to another will be
managed internally within the <code>Post</code> type. The states change in response to the
methods called by our librarys users on the <code>Post</code> instance, but they dont
have to manage the state changes directly. Also, users cant make a mistake
with the states, such as publishing a post before its reviewed.</p>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="defining-post-and-creating-a-new-instance-in-the-draft-state"></a></p>
<h4 id="defining-post-and-creating-a-new-instance"><a class="header" href="#defining-post-and-creating-a-new-instance">Defining <code>Post</code> and Creating a New Instance</a></h4>
<p>Lets get started on the implementation of the library! We know we need a
public <code>Post</code> struct that holds some content, so well start with the
definition of the struct and an associated public <code>new</code> function to create an
instance of <code>Post</code>, as shown in Listing 18-12. Well also make a private
<code>State</code> trait that will define the behavior that all state objects for a <code>Post</code>
must have.</p>
<p>Then, <code>Post</code> will hold a trait object of <code>Box&lt;dyn State&gt;</code> inside an <code>Option&lt;T&gt;</code>
in a private field named <code>state</code> to hold the state object. Youll see why the
<code>Option&lt;T&gt;</code> is necessary in a bit.</p>
<figure class="listing" id="listing-18-12">
<span class="file-name">Filename: src/lib.rs</span>
<pre><code class="language-rust noplayground">pub struct Post {
state: Option&lt;Box&lt;dyn State&gt;&gt;,
content: String,
}
impl Post {
pub fn new() -&gt; Post {
Post {
state: Some(Box::new(Draft {})),
content: String::new(),
}
}
}
trait State {}
struct Draft {}
impl State for Draft {}</code></pre>
<figcaption><a href="#listing-18-12">Listing 18-12</a>: Definition of a <code>Post</code> struct and a <code>new</code> function that creates a new <code>Post</code> instance, a <code>State</code> trait, and a <code>Draft</code> struct</figcaption>
</figure>
<p>The <code>State</code> trait defines the behavior shared by different post states. The
state objects are <code>Draft</code>, <code>PendingReview</code>, and <code>Published</code>, and they will all
implement the <code>State</code> trait. For now, the trait doesnt have any methods, and
well start by defining just the <code>Draft</code> state because that is the state we
want a post to start in.</p>
<p>When we create a new <code>Post</code>, we set its <code>state</code> field to a <code>Some</code> value that
holds a <code>Box</code>. This <code>Box</code> points to a new instance of the <code>Draft</code> struct. This
ensures that whenever we create a new instance of <code>Post</code>, it will start out as
a draft. Because the <code>state</code> field of <code>Post</code> is private, there is no way to
create a <code>Post</code> in any other state! In the <code>Post::new</code> function, we set the
<code>content</code> field to a new, empty <code>String</code>.</p>
<h4 id="storing-the-text-of-the-post-content"><a class="header" href="#storing-the-text-of-the-post-content">Storing the Text of the Post Content</a></h4>
<p>We saw in Listing 18-11 that we want to be able to call a method named
<code>add_text</code> and pass it a <code>&amp;str</code> that is then added as the text content of the
blog post. We implement this as a method, rather than exposing the <code>content</code>
field as <code>pub</code>, so that later we can implement a method that will control how
the <code>content</code> fields data is read. The <code>add_text</code> method is pretty
straightforward, so lets add the implementation in Listing 18-13 to the <code>impl Post</code> block.</p>
<figure class="listing" id="listing-18-13">
<span class="file-name">Filename: src/lib.rs</span>
<pre><code class="language-rust noplayground"><span class="boring">pub struct Post {
</span><span class="boring"> state: Option&lt;Box&lt;dyn State&gt;&gt;,
</span><span class="boring"> content: String,
</span><span class="boring">}
</span><span class="boring">
</span>impl Post {
// --snip--
<span class="boring"> pub fn new() -&gt; Post {
</span><span class="boring"> Post {
</span><span class="boring"> state: Some(Box::new(Draft {})),
</span><span class="boring"> content: String::new(),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span> pub fn add_text(&amp;mut self, text: &amp;str) {
self.content.push_str(text);
}
}
<span class="boring">
</span><span class="boring">trait State {}
</span><span class="boring">
</span><span class="boring">struct Draft {}
</span><span class="boring">
</span><span class="boring">impl State for Draft {}</span></code></pre>
<figcaption><a href="#listing-18-13">Listing 18-13</a>: Implementing the <code>add_text</code> method to add text to a posts <code>content</code></figcaption>
</figure>
<p>The <code>add_text</code> method takes a mutable reference to <code>self</code> because were
changing the <code>Post</code> instance that were calling <code>add_text</code> on. We then call
<code>push_str</code> on the <code>String</code> in <code>content</code> and pass the <code>text</code> argument to add to
the saved <code>content</code>. This behavior doesnt depend on the state the post is in,
so its not part of the state pattern. The <code>add_text</code> method doesnt interact
with the <code>state</code> field at all, but it is part of the behavior we want to
support.</p>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="ensuring-the-content-of-a-draft-post-is-empty"></a></p>
<h4 id="ensuring-that-the-content-of-a-draft-post-is-empty"><a class="header" href="#ensuring-that-the-content-of-a-draft-post-is-empty">Ensuring That the Content of a Draft Post Is Empty</a></h4>
<p>Even after weve called <code>add_text</code> and added some content to our post, we still
want the <code>content</code> method to return an empty string slice because the post is
still in the draft state, as shown by the first <code>assert_eq!</code> in Listing 18-11.
For now, lets implement the <code>content</code> method with the simplest thing that will
fulfill this requirement: always returning an empty string slice. Well change
this later once we implement the ability to change a posts state so that 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.</p>
<figure class="listing" id="listing-18-14">
<span class="file-name">Filename: src/lib.rs</span>
<pre><code class="language-rust noplayground"><span class="boring">pub struct Post {
</span><span class="boring"> state: Option&lt;Box&lt;dyn State&gt;&gt;,
</span><span class="boring"> content: String,
</span><span class="boring">}
</span><span class="boring">
</span>impl Post {
// --snip--
<span class="boring"> pub fn new() -&gt; Post {
</span><span class="boring"> Post {
</span><span class="boring"> state: Some(Box::new(Draft {})),
</span><span class="boring"> content: String::new(),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn add_text(&amp;mut self, text: &amp;str) {
</span><span class="boring"> self.content.push_str(text);
</span><span class="boring"> }
</span><span class="boring">
</span> pub fn content(&amp;self) -&gt; &amp;str {
""
}
}
<span class="boring">
</span><span class="boring">trait State {}
</span><span class="boring">
</span><span class="boring">struct Draft {}
</span><span class="boring">
</span><span class="boring">impl State for Draft {}</span></code></pre>
<figcaption><a href="#listing-18-14">Listing 18-14</a>: Adding a placeholder implementation for the <code>content</code> method on <code>Post</code> that always returns an empty string slice</figcaption>
</figure>
<p>With this added <code>content</code> method, everything in Listing 18-11 through the first
<code>assert_eq!</code> works as intended.</p>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="requesting-a-review-of-the-post-changes-its-state"></a>
<a id="requesting-a-review-changes-the-posts-state"></a></p>
<h4 id="requesting-a-review-which-changes-the-posts-state"><a class="header" href="#requesting-a-review-which-changes-the-posts-state">Requesting a Review, Which Changes the Posts State</a></h4>
<p>Next, we need to add functionality to request a review of a post, which should
change its state from <code>Draft</code> to <code>PendingReview</code>. Listing 18-15 shows this code.</p>
<figure class="listing" id="listing-18-15">
<span class="file-name">Filename: src/lib.rs</span>
<pre><code class="language-rust noplayground"><span class="boring">pub struct Post {
</span><span class="boring"> state: Option&lt;Box&lt;dyn State&gt;&gt;,
</span><span class="boring"> content: String,
</span><span class="boring">}
</span><span class="boring">
</span>impl Post {
// --snip--
<span class="boring"> pub fn new() -&gt; Post {
</span><span class="boring"> Post {
</span><span class="boring"> state: Some(Box::new(Draft {})),
</span><span class="boring"> content: String::new(),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn add_text(&amp;mut self, text: &amp;str) {
</span><span class="boring"> self.content.push_str(text);
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn content(&amp;self) -&gt; &amp;str {
</span><span class="boring"> ""
</span><span class="boring"> }
</span><span class="boring">
</span> pub fn request_review(&amp;mut self) {
if let Some(s) = self.state.take() {
self.state = Some(s.request_review())
}
}
}
trait State {
fn request_review(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt;;
}
struct Draft {}
impl State for Draft {
fn request_review(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
Box::new(PendingReview {})
}
}
struct PendingReview {}
impl State for PendingReview {
fn request_review(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
self
}
}</code></pre>
<figcaption><a href="#listing-18-15">Listing 18-15</a>: Implementing <code>request_review</code> methods on <code>Post</code> and the <code>State</code> trait</figcaption>
</figure>
<p>We give <code>Post</code> a public method named <code>request_review</code> that will take a mutable
reference to <code>self</code>. Then, we call an internal <code>request_review</code> method on the
current state of <code>Post</code>, and this second <code>request_review</code> method consumes the
current state and returns a new state.</p>
<p>We add the <code>request_review</code> method to the <code>State</code> trait; all types that
implement the trait will now need to implement the <code>request_review</code> method.
Note that rather than having <code>self</code>, <code>&amp;self</code>, or <code>&amp;mut self</code> as the first
parameter of the method, we have <code>self: Box&lt;Self&gt;</code>. This syntax means the
method is only valid when called on a <code>Box</code> holding the type. This syntax takes
ownership of <code>Box&lt;Self&gt;</code>, invalidating the old state so that the state value of
the <code>Post</code> can transform into a new state.</p>
<p>To consume the old state, the <code>request_review</code> method needs to take ownership
of the state value. This is where the <code>Option</code> in the <code>state</code> field of <code>Post</code>
comes in: We call the <code>take</code> method to take the <code>Some</code> value out of the <code>state</code>
field and leave a <code>None</code> in its place because Rust doesnt let us have
unpopulated fields in structs. This lets us move the <code>state</code> value out of
<code>Post</code> rather than borrowing it. Then, well set the posts <code>state</code> value to
the result of this operation.</p>
<p>We need to set <code>state</code> to <code>None</code> temporarily rather than setting it directly
with code like <code>self.state = self.state.request_review();</code> to get ownership of
the <code>state</code> value. This ensures that <code>Post</code> cant use the old <code>state</code> value
after weve transformed it into a new state.</p>
<p>The <code>request_review</code> method on <code>Draft</code> returns a new, boxed instance of a new
<code>PendingReview</code> struct, which represents the state when a post is waiting for a
review. The <code>PendingReview</code> struct also implements the <code>request_review</code> method
but doesnt do any transformations. Rather, it returns itself because when we
request a review on a post already in the <code>PendingReview</code> state, it should stay
in the <code>PendingReview</code> state.</p>
<p>Now we can start seeing the advantages of the state pattern: The
<code>request_review</code> method on <code>Post</code> is the same no matter its <code>state</code> value. Each
state is responsible for its own rules.</p>
<p>Well leave the <code>content</code> method on <code>Post</code> as is, returning an empty string
slice. We can now have a <code>Post</code> in the <code>PendingReview</code> state as well as in the
<code>Draft</code> state, but we want the same behavior in the <code>PendingReview</code> state.
Listing 18-11 now works up to the second <code>assert_eq!</code> call!</p>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="adding-the-approve-method-that-changes-the-behavior-of-content"></a>
<a id="adding-approve-to-change-the-behavior-of-content"></a></p>
<h4 id="adding-approve-to-change-contents-behavior"><a class="header" href="#adding-approve-to-change-contents-behavior">Adding <code>approve</code> to Change <code>content</code>s Behavior</a></h4>
<p>The <code>approve</code> method will be similar to the <code>request_review</code> method: It will
set <code>state</code> to the value that the current state says it should have when that
state is approved, as shown in Listing 18-16.</p>
<figure class="listing" id="listing-18-16">
<span class="file-name">Filename: src/lib.rs</span>
<pre><code class="language-rust noplayground"><span class="boring">pub struct Post {
</span><span class="boring"> state: Option&lt;Box&lt;dyn State&gt;&gt;,
</span><span class="boring"> content: String,
</span><span class="boring">}
</span><span class="boring">
</span>impl Post {
// --snip--
<span class="boring"> pub fn new() -&gt; Post {
</span><span class="boring"> Post {
</span><span class="boring"> state: Some(Box::new(Draft {})),
</span><span class="boring"> content: String::new(),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn add_text(&amp;mut self, text: &amp;str) {
</span><span class="boring"> self.content.push_str(text);
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn content(&amp;self) -&gt; &amp;str {
</span><span class="boring"> ""
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn request_review(&amp;mut self) {
</span><span class="boring"> if let Some(s) = self.state.take() {
</span><span class="boring"> self.state = Some(s.request_review())
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span> pub fn approve(&amp;mut self) {
if let Some(s) = self.state.take() {
self.state = Some(s.approve())
}
}
}
trait State {
fn request_review(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt;;
fn approve(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt;;
}
struct Draft {}
impl State for Draft {
// --snip--
<span class="boring"> fn request_review(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
</span><span class="boring"> Box::new(PendingReview {})
</span><span class="boring"> }
</span><span class="boring">
</span> fn approve(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
self
}
}
struct PendingReview {}
impl State for PendingReview {
// --snip--
<span class="boring"> fn request_review(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">
</span> fn approve(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
Box::new(Published {})
}
}
struct Published {}
impl State for Published {
fn request_review(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
self
}
fn approve(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
self
}
}</code></pre>
<figcaption><a href="#listing-18-16">Listing 18-16</a>: Implementing the <code>approve</code> method on <code>Post</code> and the <code>State</code> trait</figcaption>
</figure>
<p>We add the <code>approve</code> method to the <code>State</code> trait and add a new struct that
implements <code>State</code>, the <code>Published</code> state.</p>
<p>Similar to the way <code>request_review</code> on <code>PendingReview</code> works, if we call the
<code>approve</code> method on a <code>Draft</code>, it will have no effect because <code>approve</code> will
return <code>self</code>. When we call <code>approve</code> on <code>PendingReview</code>, it returns a new,
boxed instance of the <code>Published</code> struct. The <code>Published</code> struct implements the
<code>State</code> trait, and for both the <code>request_review</code> method and the <code>approve</code>
method, it returns itself because the post should stay in the <code>Published</code> state
in those cases.</p>
<p>Now we need to update the <code>content</code> method on <code>Post</code>. We want the value
returned from <code>content</code> to depend on the current state of the <code>Post</code>, so were
going to have the <code>Post</code> delegate to a <code>content</code> method defined on its <code>state</code>,
as shown in Listing 18-17.</p>
<figure class="listing" id="listing-18-17">
<span class="file-name">Filename: src/lib.rs</span>
<pre><code class="language-rust ignore does_not_compile"><span class="boring">pub struct Post {
</span><span class="boring"> state: Option&lt;Box&lt;dyn State&gt;&gt;,
</span><span class="boring"> content: String,
</span><span class="boring">}
</span><span class="boring">
</span>impl Post {
// --snip--
<span class="boring"> pub fn new() -&gt; Post {
</span><span class="boring"> Post {
</span><span class="boring"> state: Some(Box::new(Draft {})),
</span><span class="boring"> content: String::new(),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn add_text(&amp;mut self, text: &amp;str) {
</span><span class="boring"> self.content.push_str(text);
</span><span class="boring"> }
</span><span class="boring">
</span> pub fn content(&amp;self) -&gt; &amp;str {
self.state.as_ref().unwrap().content(self)
}
// --snip--
<span class="boring">
</span><span class="boring"> pub fn request_review(&amp;mut self) {
</span><span class="boring"> if let Some(s) = self.state.take() {
</span><span class="boring"> self.state = Some(s.request_review())
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn approve(&amp;mut self) {
</span><span class="boring"> if let Some(s) = self.state.take() {
</span><span class="boring"> self.state = Some(s.approve())
</span><span class="boring"> }
</span><span class="boring"> }
</span>}
<span class="boring">
</span><span class="boring">trait State {
</span><span class="boring"> fn request_review(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt;;
</span><span class="boring"> fn approve(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt;;
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">struct Draft {}
</span><span class="boring">
</span><span class="boring">impl State for Draft {
</span><span class="boring"> fn request_review(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
</span><span class="boring"> Box::new(PendingReview {})
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn approve(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">struct PendingReview {}
</span><span class="boring">
</span><span class="boring">impl State for PendingReview {
</span><span class="boring"> fn request_review(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn approve(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
</span><span class="boring"> Box::new(Published {})
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">struct Published {}
</span><span class="boring">
</span><span class="boring">impl State for Published {
</span><span class="boring"> fn request_review(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn approve(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">}</span></code></pre>
<figcaption><a href="#listing-18-17">Listing 18-17</a>: Updating the <code>content</code> method on <code>Post</code> to delegate to a <code>content</code> method on <code>State</code></figcaption>
</figure>
<p>Because the goal is to keep all of these rules inside the structs that
implement <code>State</code>, we call a <code>content</code> method on the value in <code>state</code> and pass
the post instance (that is, <code>self</code>) as an argument. Then, we return the value
thats returned from using the <code>content</code> method on the <code>state</code> value.</p>
<p>We call the <code>as_ref</code> method on the <code>Option</code> because we want a reference to the
value inside the <code>Option</code> rather than ownership of the value. Because <code>state</code> is
an <code>Option&lt;Box&lt;dyn State&gt;&gt;</code>, when we call <code>as_ref</code>, an <code>Option&lt;&amp;Box&lt;dyn State&gt;&gt;</code> is returned. If we didnt call <code>as_ref</code>, we would get an error because
we cant move <code>state</code> out of the borrowed <code>&amp;self</code> of the function parameter.</p>
<p>We then call the <code>unwrap</code> method, which we know will never panic because we
know the methods on <code>Post</code> ensure that <code>state</code> will always contain a <code>Some</code>
value when those methods are done. This is one of the cases we talked about in
the <a href="../ch09/ch09-03-to-panic-or-not-to-panic.html#cases-in-which-you-have-more-information-than-the-compiler">“When You Have More Information Than the
Compiler”</a><!-- ignore --> section of Chapter 9 when we
know that a <code>None</code> value is never possible, even though the compiler isnt able
to understand that.</p>
<p>At this point, when we call <code>content</code> on the <code>&amp;Box&lt;dyn State&gt;</code>, deref coercion
will take effect on the <code>&amp;</code> and the <code>Box</code> so that the <code>content</code> method will
ultimately be called on the type that implements the <code>State</code> trait. That means
we need to add <code>content</code> to the <code>State</code> trait definition, and that is where
well put the logic for what content to return depending on which state we
have, as shown in Listing 18-18.</p>
<figure class="listing" id="listing-18-18">
<span class="file-name">Filename: src/lib.rs</span>
<pre><code class="language-rust noplayground"><span class="boring">pub struct Post {
</span><span class="boring"> state: Option&lt;Box&lt;dyn State&gt;&gt;,
</span><span class="boring"> content: String,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Post {
</span><span class="boring"> pub fn new() -&gt; Post {
</span><span class="boring"> Post {
</span><span class="boring"> state: Some(Box::new(Draft {})),
</span><span class="boring"> content: String::new(),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn add_text(&amp;mut self, text: &amp;str) {
</span><span class="boring"> self.content.push_str(text);
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn content(&amp;self) -&gt; &amp;str {
</span><span class="boring"> self.state.as_ref().unwrap().content(self)
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn request_review(&amp;mut self) {
</span><span class="boring"> if let Some(s) = self.state.take() {
</span><span class="boring"> self.state = Some(s.request_review())
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn approve(&amp;mut self) {
</span><span class="boring"> if let Some(s) = self.state.take() {
</span><span class="boring"> self.state = Some(s.approve())
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span>trait State {
// --snip--
<span class="boring"> fn request_review(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt;;
</span><span class="boring"> fn approve(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt;;
</span><span class="boring">
</span> fn content&lt;'a&gt;(&amp;self, post: &amp;'a Post) -&gt; &amp;'a str {
""
}
}
// --snip--
<span class="boring">
</span><span class="boring">struct Draft {}
</span><span class="boring">
</span><span class="boring">impl State for Draft {
</span><span class="boring"> fn request_review(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
</span><span class="boring"> Box::new(PendingReview {})
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn approve(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">struct PendingReview {}
</span><span class="boring">
</span><span class="boring">impl State for PendingReview {
</span><span class="boring"> fn request_review(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn approve(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
</span><span class="boring"> Box::new(Published {})
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span>struct Published {}
impl State for Published {
// --snip--
<span class="boring"> fn request_review(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn approve(self: Box&lt;Self&gt;) -&gt; Box&lt;dyn State&gt; {
</span><span class="boring"> self
</span><span class="boring"> }
</span><span class="boring">
</span> fn content&lt;'a&gt;(&amp;self, post: &amp;'a Post) -&gt; &amp;'a str {
&amp;post.content
}
}</code></pre>
<figcaption><a href="#listing-18-18">Listing 18-18</a>: Adding the <code>content</code> method to the <code>State</code> trait</figcaption>
</figure>
<p>We add a default implementation for the <code>content</code> method that returns an empty
string slice. That means we dont need to implement <code>content</code> on the <code>Draft</code>
and <code>PendingReview</code> structs. The <code>Published</code> struct will override the <code>content</code>
method and return the value in <code>post.content</code>. While convenient, having the
<code>content</code> method on <code>State</code> determine the content of the <code>Post</code> is blurring
the lines between the responsibility of <code>State</code> and the responsibility of
<code>Post</code>.</p>
<p>Note that we need lifetime annotations on this method, as we discussed in
Chapter 10. Were taking a reference to a <code>post</code> as an argument and returning a
reference to part of that <code>post</code>, so the lifetime of the returned reference is
related to the lifetime of the <code>post</code> argument.</p>
<p>And were done—all of Listing 18-11 now works! Weve implemented the state
pattern with the rules of the blog post workflow. The logic related to the
rules lives in the state objects rather than being scattered throughout <code>Post</code>.</p>
<section class="note" aria-role="note">
<h3 id="why-not-an-enum"><a class="header" href="#why-not-an-enum">Why Not An Enum?</a></h3>
<p>You may have been wondering why we didnt use an enum with the different
possible post states as variants. Thats certainly a possible solution; try it
and compare the end results to see which you prefer! One disadvantage of using
an enum is that every place that checks the value of the enum will need a
<code>match</code> expression or similar to handle every possible variant. This could get
more repetitive than this trait object solution.</p>
</section>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="trade-offs-of-the-state-pattern"></a></p>
<h4 id="evaluating-the-state-pattern"><a class="header" href="#evaluating-the-state-pattern">Evaluating the State Pattern</a></h4>
<p>Weve shown that Rust is capable of implementing the object-oriented state
pattern to encapsulate the different kinds of behavior a post should have in
each state. The methods on <code>Post</code> know nothing about the various behaviors.
Because of the way we organized the code, we have to look in only one place to
know the different ways a published post can behave: the implementation of the
<code>State</code> trait on the <code>Published</code> struct.</p>
<p>If we were to create an alternative implementation that didnt use the state
pattern, we might instead use <code>match</code> expressions in the methods on <code>Post</code> or
even in the <code>main</code> code that checks the state of the post and changes behavior
in those places. That would mean we would have to look in several places to
understand all the implications of a post being in the published state.</p>
<p>With the state pattern, the <code>Post</code> methods and the places we use <code>Post</code> dont
need <code>match</code> expressions, and to add a new state, we would only need to add a
new struct and implement the trait methods on that one struct in one location.</p>
<p>The implementation using the state pattern is easy to extend to add more
functionality. To see the simplicity of maintaining code that uses the state
pattern, try a few of these suggestions:</p>
<ul>
<li>Add a <code>reject</code> method that changes the posts state from <code>PendingReview</code> back
to <code>Draft</code>.</li>
<li>Require two calls to <code>approve</code> before the state can be changed to <code>Published</code>.</li>
<li>Allow users to add text content only when a post is in the <code>Draft</code> state.
Hint: have the state object responsible for what might change about the
content but not responsible for modifying the <code>Post</code>.</li>
</ul>
<p>One downside of the state pattern is that, because the states implement the
transitions between states, some of the states are coupled to each other. If we
add another state between <code>PendingReview</code> and <code>Published</code>, such as <code>Scheduled</code>,
we would have to change the code in <code>PendingReview</code> to transition to
<code>Scheduled</code> instead. It would be less work if <code>PendingReview</code> didnt need to
change with the addition of a new state, but that would mean switching to
another design pattern.</p>
<p>Another downside is that weve duplicated some logic. To eliminate some of the
duplication, we might try to make default implementations for the
<code>request_review</code> and <code>approve</code> methods on the <code>State</code> trait that return <code>self</code>.
However, this wouldnt work: When using <code>State</code> as a trait object, the trait
doesnt know what the concrete <code>self</code> will be exactly, so the return type isnt
known at compile time. (This is one of the dyn compatibility rules mentioned
earlier.)</p>
<p>Other duplication includes the similar implementations of the <code>request_review</code>
and <code>approve</code> methods on <code>Post</code>. Both methods use <code>Option::take</code> with the
<code>state</code> field of <code>Post</code>, and if <code>state</code> is <code>Some</code>, they delegate to the wrapped
values implementation of the same method and set the new value of the <code>state</code>
field to the result. If we had a lot of methods on <code>Post</code> that followed this
pattern, we might consider defining a macro to eliminate the repetition (see
the <a href="../ch20/ch20-05-macros.html#macros">“Macros”</a><!-- ignore --> section in Chapter 20).</p>
<p>By implementing the state pattern exactly as its defined for object-oriented
languages, were not taking as full advantage of Rusts strengths as we could.
Lets look at some changes we can make to the <code>blog</code> crate that can make
invalid states and transitions into compile-time errors.</p>
<h3 id="encoding-states-and-behavior-as-types"><a class="header" href="#encoding-states-and-behavior-as-types">Encoding States and Behavior as Types</a></h3>
<p>Well show you how to rethink the state pattern to get a different set of
trade-offs. Rather than encapsulating the states and transitions completely so
that outside code has no knowledge of them, well encode the states into
different types. Consequently, Rusts type-checking system will prevent
attempts to use draft posts where only published posts are allowed by issuing a
compiler error.</p>
<p>Lets consider the first part of <code>main</code> in Listing 18-11:</p>
<figure class="listing">
<span class="file-name">Filename: src/main.rs</span>
<pre><code class="language-rust ignore"><span class="boring">use blog::Post;
</span><span class="boring">
</span>fn main() {
let mut post = Post::new();
post.add_text("I ate a salad for lunch today");
assert_eq!("", post.content());
<span class="boring">
</span><span class="boring"> post.request_review();
</span><span class="boring"> assert_eq!("", post.content());
</span><span class="boring">
</span><span class="boring"> post.approve();
</span><span class="boring"> assert_eq!("I ate a salad for lunch today", post.content());
</span>}</code></pre>
</figure>
<p>We still enable the creation of new posts in the draft state using <code>Post::new</code>
and the ability to add text to the posts content. But instead of having a
<code>content</code> method on a draft post that returns an empty string, well make it so
that draft posts dont have the <code>content</code> method at all. That way, if we try to
get a draft posts content, well get a compiler error telling us the method
doesnt exist. As a result, it will be impossible for us to accidentally
display draft post content in production because that code wont even compile.
Listing 18-19 shows the definition of a <code>Post</code> struct and a <code>DraftPost</code> struct,
as well as methods on each.</p>
<figure class="listing" id="listing-18-19">
<span class="file-name">Filename: src/lib.rs</span>
<pre><code class="language-rust noplayground">pub struct Post {
content: String,
}
pub struct DraftPost {
content: String,
}
impl Post {
pub fn new() -&gt; DraftPost {
DraftPost {
content: String::new(),
}
}
pub fn content(&amp;self) -&gt; &amp;str {
&amp;self.content
}
}
impl DraftPost {
pub fn add_text(&amp;mut self, text: &amp;str) {
self.content.push_str(text);
}
}</code></pre>
<figcaption><a href="#listing-18-19">Listing 18-19</a>: A <code>Post</code> with a <code>content</code> method and a <code>DraftPost</code> without a <code>content</code> method</figcaption>
</figure>
<p>Both the <code>Post</code> and <code>DraftPost</code> structs have a private <code>content</code> field that
stores the blog post text. The structs no longer have the <code>state</code> field because
were moving the encoding of the state to the types of the structs. The <code>Post</code>
struct will represent a published post, and it has a <code>content</code> method that
returns the <code>content</code>.</p>
<p>We still have a <code>Post::new</code> function, but instead of returning an instance of
<code>Post</code>, it returns an instance of <code>DraftPost</code>. Because <code>content</code> is private and
there arent any functions that return <code>Post</code>, its not possible to create an
instance of <code>Post</code> right now.</p>
<p>The <code>DraftPost</code> struct has an <code>add_text</code> method, so we can add text to
<code>content</code> as before, but note that <code>DraftPost</code> does not have a <code>content</code> method
defined! So now the program ensures that all posts start as draft posts, and
draft posts dont have their content available for display. Any attempt to get
around these constraints will result in a compiler error.</p>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="implementing-transitions-as-transformations-into-different-types"></a></p>
<p>So, how do we get a published post? We want to enforce the rule that a draft
post has to be reviewed and approved before it can be published. A post in the
pending review state should still not display any content. Lets implement
these constraints by adding another struct, <code>PendingReviewPost</code>, defining the
<code>request_review</code> method on <code>DraftPost</code> to return a <code>PendingReviewPost</code> and
defining an <code>approve</code> method on <code>PendingReviewPost</code> to return a <code>Post</code>, as
shown in Listing 18-20.</p>
<figure class="listing" id="listing-18-20">
<span class="file-name">Filename: src/lib.rs</span>
<pre><code class="language-rust noplayground"><span class="boring">pub struct Post {
</span><span class="boring"> content: String,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub struct DraftPost {
</span><span class="boring"> content: String,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Post {
</span><span class="boring"> pub fn new() -&gt; DraftPost {
</span><span class="boring"> DraftPost {
</span><span class="boring"> content: String::new(),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn content(&amp;self) -&gt; &amp;str {
</span><span class="boring"> &amp;self.content
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span>impl DraftPost {
// --snip--
<span class="boring"> pub fn add_text(&amp;mut self, text: &amp;str) {
</span><span class="boring"> self.content.push_str(text);
</span><span class="boring"> }
</span><span class="boring">
</span> pub fn request_review(self) -&gt; PendingReviewPost {
PendingReviewPost {
content: self.content,
}
}
}
pub struct PendingReviewPost {
content: String,
}
impl PendingReviewPost {
pub fn approve(self) -&gt; Post {
Post {
content: self.content,
}
}
}</code></pre>
<figcaption><a href="#listing-18-20">Listing 18-20</a>: A <code>PendingReviewPost</code> that gets created by calling <code>request_review</code> on <code>DraftPost</code> and an <code>approve</code> method that turns a <code>PendingReviewPost</code> into a published <code>Post</code></figcaption>
</figure>
<p>The <code>request_review</code> and <code>approve</code> methods take ownership of <code>self</code>, thus
consuming the <code>DraftPost</code> and <code>PendingReviewPost</code> instances and transforming
them into a <code>PendingReviewPost</code> and a published <code>Post</code>, respectively. This way,
we wont have any lingering <code>DraftPost</code> instances after weve called
<code>request_review</code> on them, and so forth. The <code>PendingReviewPost</code> struct doesnt
have a <code>content</code> method defined on it, so attempting to read its content
results in a compiler error, as with <code>DraftPost</code>. Because the only way to get a
published <code>Post</code> instance that does have a <code>content</code> method defined is to call
the <code>approve</code> method on a <code>PendingReviewPost</code>, and the only way to get a
<code>PendingReviewPost</code> is to call the <code>request_review</code> method on a <code>DraftPost</code>,
weve now encoded the blog post workflow into the type system.</p>
<p>But we also have to make some small changes to <code>main</code>. The <code>request_review</code> and
<code>approve</code> methods return new instances rather than modifying the struct theyre
called on, so we need to add more <code>let post =</code> shadowing assignments to save
the returned instances. We also cant have the assertions about the draft and
pending review posts contents be empty strings, nor do we need them: We cant
compile code that tries to use the content of posts in those states any longer.
The updated code in <code>main</code> is shown in Listing 18-21.</p>
<figure class="listing" id="listing-18-21">
<span class="file-name">Filename: src/main.rs</span>
<pre><code class="language-rust ignore">use blog::Post;
fn main() {
let mut post = Post::new();
post.add_text("I ate a salad for lunch today");
let post = post.request_review();
let post = post.approve();
assert_eq!("I ate a salad for lunch today", post.content());
}</code></pre>
<figcaption><a href="#listing-18-21">Listing 18-21</a>: Modifications to <code>main</code> to use the new implementation of the blog post workflow</figcaption>
</figure>
<p>The changes we needed to make to <code>main</code> to reassign <code>post</code> mean that this
implementation doesnt quite follow the object-oriented state pattern anymore:
The transformations between the states are no longer encapsulated entirely
within the <code>Post</code> implementation. However, our gain is that invalid states are
now impossible because of the type system and the type checking that happens at
compile time! This ensures that certain bugs, such as display of the content of
an unpublished post, will be discovered before they make it to production.</p>
<p>Try the tasks suggested at the start of this section on the <code>blog</code> crate as it
is after Listing 18-21 to see what you think about the design of this version
of the code. Note that some of the tasks might be completed already in this
design.</p>
<p>Weve seen that even though Rust is capable of implementing object-oriented
design patterns, other patterns, such as encoding state into the type system,
are also available in Rust. These patterns have different trade-offs. Although
you might be very familiar with object-oriented patterns, rethinking the
problem to take advantage of Rusts features can provide benefits, such as
preventing some bugs at compile time. Object-oriented patterns wont always be
the best solution in Rust due to certain features, like ownership, that
object-oriented languages dont have.</p>
<h2 id="summary"><a class="header" href="#summary">Summary</a></h2>
<p>Regardless of whether you think Rust is an object-oriented language after
reading this chapter, you now know that you can use trait objects to get some
object-oriented features in Rust. Dynamic dispatch can give your code some
flexibility in exchange for a bit of runtime performance. You can use this
flexibility to implement object-oriented patterns that can help your codes
maintainability. Rust also has other features, like ownership, that
object-oriented languages dont have. An object-oriented pattern wont always
be the best way to take advantage of Rusts strengths, but it is an available
option.</p>
<p>Next, well look at patterns, which are another of Rusts features that enable
lots of flexibility. Weve looked at them briefly throughout the book but
havent seen their full capability yet. Lets go!</p>
</body>
</html>

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Redirecting...</title>
</head>
<body>
<p>Redirecting to... <a href="../ch19/ch19-03-pattern-syntax.html">ch19-03-pattern-syntax.html</a>.</p>
</body>
</html>