300 lines
16 KiB
HTML
300 lines
16 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>An Example Program Using Structs</title>
|
||
</head>
|
||
<body>
|
||
<h2 id="an-example-program-using-structs"><a class="header" href="#an-example-program-using-structs">An Example Program Using Structs</a></h2>
|
||
<p>To understand when we might want to use structs, let’s write a program that
|
||
calculates the area of a rectangle. We’ll start by using single variables and
|
||
then refactor the program until we’re using structs instead.</p>
|
||
<p>Let’s make a new binary project with Cargo called <em>rectangles</em> that will take
|
||
the width and height of a rectangle specified in pixels and calculate the area
|
||
of the rectangle. Listing 5-8 shows a short program with one way of doing
|
||
exactly that in our project’s <em>src/main.rs</em>.</p>
|
||
<figure class="listing" id="listing-5-8">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust edition2024">fn main() {
|
||
let width1 = 30;
|
||
let height1 = 50;
|
||
|
||
println!(
|
||
"The area of the rectangle is {} square pixels.",
|
||
area(width1, height1)
|
||
);
|
||
}
|
||
|
||
fn area(width: u32, height: u32) -> u32 {
|
||
width * height
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-5-8">Listing 5-8</a>: Calculating the area of a rectangle specified by separate width and height variables</figcaption>
|
||
</figure>
|
||
<p>Now, run this program using <code>cargo run</code>:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling rectangles v0.1.0 (file:///projects/rectangles)
|
||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.42s
|
||
Running `target/debug/rectangles`
|
||
The area of the rectangle is 1500 square pixels.
|
||
</code></pre>
|
||
<p>This code succeeds in figuring out the area of the rectangle by calling the
|
||
<code>area</code> function with each dimension, but we can do more to make this code clear
|
||
and readable.</p>
|
||
<p>The issue with this code is evident in the signature of <code>area</code>:</p>
|
||
<pre><code class="language-rust ignore"><span class="boring">fn main() {
|
||
</span><span class="boring"> let width1 = 30;
|
||
</span><span class="boring"> let height1 = 50;
|
||
</span><span class="boring">
|
||
</span><span class="boring"> println!(
|
||
</span><span class="boring"> "The area of the rectangle is {} square pixels.",
|
||
</span><span class="boring"> area(width1, height1)
|
||
</span><span class="boring"> );
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span>fn area(width: u32, height: u32) -> u32 {
|
||
<span class="boring"> width * height
|
||
</span><span class="boring">}</span></code></pre>
|
||
<p>The <code>area</code> function is supposed to calculate the area of one rectangle, but the
|
||
function we wrote has two parameters, and it’s not clear anywhere in our
|
||
program that the parameters are related. It would be more readable and more
|
||
manageable to group width and height together. We’ve already discussed one way
|
||
we might do that in <a href="../ch03/ch03-02-data-types.html#the-tuple-type">“The Tuple Type”</a><!-- ignore --> section
|
||
of Chapter 3: by using tuples.</p>
|
||
<h3 id="refactoring-with-tuples"><a class="header" href="#refactoring-with-tuples">Refactoring with Tuples</a></h3>
|
||
<p>Listing 5-9 shows another version of our program that uses tuples.</p>
|
||
<figure class="listing" id="listing-5-9">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust edition2024">fn main() {
|
||
let rect1 = (30, 50);
|
||
|
||
println!(
|
||
"The area of the rectangle is {} square pixels.",
|
||
area(rect1)
|
||
);
|
||
}
|
||
|
||
fn area(dimensions: (u32, u32)) -> u32 {
|
||
dimensions.0 * dimensions.1
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-5-9">Listing 5-9</a>: Specifying the width and height of the rectangle with a tuple</figcaption>
|
||
</figure>
|
||
<p>In one way, this program is better. Tuples let us add a bit of structure, and
|
||
we’re now passing just one argument. But in another way, this version is less
|
||
clear: Tuples don’t name their elements, so we have to index into the parts of
|
||
the tuple, making our calculation less obvious.</p>
|
||
<p>Mixing up the width and height wouldn’t matter for the area calculation, but if
|
||
we want to draw the rectangle on the screen, it would matter! We would have to
|
||
keep in mind that <code>width</code> is the tuple index <code>0</code> and <code>height</code> is the tuple
|
||
index <code>1</code>. This would be even harder for someone else to figure out and keep in
|
||
mind if they were to use our code. Because we haven’t conveyed the meaning of
|
||
our data in our code, it’s now easier to introduce errors.</p>
|
||
<!-- Old headings. Do not remove or links may break. -->
|
||
<p><a id="refactoring-with-structs-adding-more-meaning"></a></p>
|
||
<h3 id="refactoring-with-structs"><a class="header" href="#refactoring-with-structs">Refactoring with Structs</a></h3>
|
||
<p>We use structs to add meaning by labeling the data. We can transform the tuple
|
||
we’re using into a struct with a name for the whole as well as names for the
|
||
parts, as shown in Listing 5-10.</p>
|
||
<figure class="listing" id="listing-5-10">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust edition2024">struct Rectangle {
|
||
width: u32,
|
||
height: u32,
|
||
}
|
||
|
||
fn main() {
|
||
let rect1 = Rectangle {
|
||
width: 30,
|
||
height: 50,
|
||
};
|
||
|
||
println!(
|
||
"The area of the rectangle is {} square pixels.",
|
||
area(&rect1)
|
||
);
|
||
}
|
||
|
||
fn area(rectangle: &Rectangle) -> u32 {
|
||
rectangle.width * rectangle.height
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-5-10">Listing 5-10</a>: Defining a <code>Rectangle</code> struct</figcaption>
|
||
</figure>
|
||
<p>Here, we’ve defined a struct and named it <code>Rectangle</code>. Inside the curly
|
||
brackets, we defined the fields as <code>width</code> and <code>height</code>, both of which have
|
||
type <code>u32</code>. Then, in <code>main</code>, we created a particular instance of <code>Rectangle</code>
|
||
that has a width of <code>30</code> and a height of <code>50</code>.</p>
|
||
<p>Our <code>area</code> function is now defined with one parameter, which we’ve named
|
||
<code>rectangle</code>, whose type is an immutable borrow of a struct <code>Rectangle</code>
|
||
instance. As mentioned in Chapter 4, we want to borrow the struct rather than
|
||
take ownership of it. This way, <code>main</code> retains its ownership and can continue
|
||
using <code>rect1</code>, which is the reason we use the <code>&</code> in the function signature and
|
||
where we call the function.</p>
|
||
<p>The <code>area</code> function accesses the <code>width</code> and <code>height</code> fields of the <code>Rectangle</code>
|
||
instance (note that accessing fields of a borrowed struct instance does not
|
||
move the field values, which is why you often see borrows of structs). Our
|
||
function signature for <code>area</code> now says exactly what we mean: Calculate the area
|
||
of <code>Rectangle</code>, using its <code>width</code> and <code>height</code> fields. This conveys that the
|
||
width and height are related to each other, and it gives descriptive names to
|
||
the values rather than using the tuple index values of <code>0</code> and <code>1</code>. This is a
|
||
win for clarity.</p>
|
||
<!-- Old headings. Do not remove or links may break. -->
|
||
<p><a id="adding-useful-functionality-with-derived-traits"></a></p>
|
||
<h3 id="adding-functionality-with-derived-traits"><a class="header" href="#adding-functionality-with-derived-traits">Adding Functionality with Derived Traits</a></h3>
|
||
<p>It’d be useful to be able to print an instance of <code>Rectangle</code> while we’re
|
||
debugging our program and see the values for all its fields. Listing 5-11 tries
|
||
using the <a href="../std/macro.println.html"><code>println!</code> macro</a><!-- ignore --> as we have used in
|
||
previous chapters. This won’t work, however.</p>
|
||
<figure class="listing" id="listing-5-11">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre><code class="language-rust ignore does_not_compile">struct Rectangle {
|
||
width: u32,
|
||
height: u32,
|
||
}
|
||
|
||
fn main() {
|
||
let rect1 = Rectangle {
|
||
width: 30,
|
||
height: 50,
|
||
};
|
||
|
||
println!("rect1 is {rect1}");
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-5-11">Listing 5-11</a>: Attempting to print a <code>Rectangle</code> instance</figcaption>
|
||
</figure>
|
||
<p>When we compile this code, we get an error with this core message:</p>
|
||
<pre><code class="language-text">error[E0277]: `Rectangle` doesn't implement `std::fmt::Display`
|
||
</code></pre>
|
||
<p>The <code>println!</code> macro can do many kinds of formatting, and by default, the curly
|
||
brackets tell <code>println!</code> to use formatting known as <code>Display</code>: output intended
|
||
for direct end user consumption. The primitive types we’ve seen so far
|
||
implement <code>Display</code> by default because there’s only one way you’d want to show
|
||
a <code>1</code> or any other primitive type to a user. But with structs, the way
|
||
<code>println!</code> should format the output is less clear because there are more
|
||
display possibilities: Do you want commas or not? Do you want to print the
|
||
curly brackets? Should all the fields be shown? Due to this ambiguity, Rust
|
||
doesn’t try to guess what we want, and structs don’t have a provided
|
||
implementation of <code>Display</code> to use with <code>println!</code> and the <code>{}</code> placeholder.</p>
|
||
<p>If we continue reading the errors, we’ll find this helpful note:</p>
|
||
<pre><code class="language-text"> | |`Rectangle` cannot be formatted with the default formatter
|
||
| required by this formatting parameter
|
||
</code></pre>
|
||
<p>Let’s try it! The <code>println!</code> macro call will now look like <code>println!("rect1 is {rect1:?}");</code>. Putting the specifier <code>:?</code> inside the curly brackets tells
|
||
<code>println!</code> we want to use an output format called <code>Debug</code>. The <code>Debug</code> trait
|
||
enables us to print our struct in a way that is useful for developers so that
|
||
we can see its value while we’re debugging our code.</p>
|
||
<p>Compile the code with this change. Drat! We still get an error:</p>
|
||
<pre><code class="language-text">error[E0277]: `Rectangle` doesn't implement `Debug`
|
||
</code></pre>
|
||
<p>But again, the compiler gives us a helpful note:</p>
|
||
<pre><code class="language-text"> | required by this formatting parameter
|
||
|
|
||
</code></pre>
|
||
<p>Rust <em>does</em> include functionality to print out debugging information, but we
|
||
have to explicitly opt in to make that functionality available for our struct.
|
||
To do that, we add the outer attribute <code>#[derive(Debug)]</code> just before the
|
||
struct definition, as shown in Listing 5-12.</p>
|
||
<figure class="listing" id="listing-5-12">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust edition2024">#[derive(Debug)]
|
||
struct Rectangle {
|
||
width: u32,
|
||
height: u32,
|
||
}
|
||
|
||
fn main() {
|
||
let rect1 = Rectangle {
|
||
width: 30,
|
||
height: 50,
|
||
};
|
||
|
||
println!("rect1 is {rect1:?}");
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-5-12">Listing 5-12</a>: Adding the attribute to derive the <code>Debug</code> trait and printing the <code>Rectangle</code> instance using debug formatting</figcaption>
|
||
</figure>
|
||
<p>Now when we run the program, we won’t get any errors, and we’ll see the
|
||
following output:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling rectangles v0.1.0 (file:///projects/rectangles)
|
||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.48s
|
||
Running `target/debug/rectangles`
|
||
rect1 is Rectangle { width: 30, height: 50 }
|
||
</code></pre>
|
||
<p>Nice! It’s not the prettiest output, but it shows the values of all the fields
|
||
for this instance, which would definitely help during debugging. When we have
|
||
larger structs, it’s useful to have output that’s a bit easier to read; in
|
||
those cases, we can use <code>{:#?}</code> instead of <code>{:?}</code> in the <code>println!</code> string. In
|
||
this example, using the <code>{:#?}</code> style will output the following:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling rectangles v0.1.0 (file:///projects/rectangles)
|
||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.48s
|
||
Running `target/debug/rectangles`
|
||
rect1 is Rectangle {
|
||
width: 30,
|
||
height: 50,
|
||
}
|
||
</code></pre>
|
||
<p>Another way to print out a value using the <code>Debug</code> format is to use the <a href="../std/macro.dbg.html"><code>dbg!</code>
|
||
macro</a><!-- ignore -->, which takes ownership of an expression (as opposed
|
||
to <code>println!</code>, which takes a reference), prints the file and line number of
|
||
where that <code>dbg!</code> macro call occurs in your code along with the resultant value
|
||
of that expression, and returns ownership of the value.</p>
|
||
<section class="note" aria-role="note">
|
||
<p>Note: Calling the <code>dbg!</code> macro prints to the standard error console stream
|
||
(<code>stderr</code>), as opposed to <code>println!</code>, which prints to the standard output
|
||
console stream (<code>stdout</code>). We’ll talk more about <code>stderr</code> and <code>stdout</code> in the
|
||
<a href="../ch12/ch12-06-writing-to-stderr-instead-of-stdout.html">“Redirecting Errors to Standard Error” section in Chapter
|
||
12</a><!-- ignore -->.</p>
|
||
</section>
|
||
<p>Here’s an example where we’re interested in the value that gets assigned to the
|
||
<code>width</code> field, as well as the value of the whole struct in <code>rect1</code>:</p>
|
||
<pre class="playground"><code class="language-rust edition2024">#[derive(Debug)]
|
||
struct Rectangle {
|
||
width: u32,
|
||
height: u32,
|
||
}
|
||
|
||
fn main() {
|
||
let scale = 2;
|
||
let rect1 = Rectangle {
|
||
width: dbg!(30 * scale),
|
||
height: 50,
|
||
};
|
||
|
||
dbg!(&rect1);
|
||
}</code></pre>
|
||
<p>We can put <code>dbg!</code> around the expression <code>30 * scale</code> and, because <code>dbg!</code>
|
||
returns ownership of the expression’s value, the <code>width</code> field will get the
|
||
same value as if we didn’t have the <code>dbg!</code> call there. We don’t want <code>dbg!</code> to
|
||
take ownership of <code>rect1</code>, so we use a reference to <code>rect1</code> in the next call.
|
||
Here’s what the output of this example looks like:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling rectangles v0.1.0 (file:///projects/rectangles)
|
||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.61s
|
||
Running `target/debug/rectangles`
|
||
[src/main.rs:10:16] 30 * scale = 60
|
||
[src/main.rs:14:5] &rect1 = Rectangle {
|
||
width: 60,
|
||
height: 50,
|
||
}
|
||
</code></pre>
|
||
<p>We can see the first bit of output came from <em>src/main.rs</em> line 10 where we’re
|
||
debugging the expression <code>30 * scale</code>, and its resultant value is <code>60</code> (the
|
||
<code>Debug</code> formatting implemented for integers is to print only their value). The
|
||
<code>dbg!</code> call on line 14 of <em>src/main.rs</em> outputs the value of <code>&rect1</code>, which is
|
||
the <code>Rectangle</code> struct. This output uses the pretty <code>Debug</code> formatting of the
|
||
<code>Rectangle</code> type. The <code>dbg!</code> macro can be really helpful when you’re trying to
|
||
figure out what your code is doing!</p>
|
||
<p>In addition to the <code>Debug</code> trait, Rust has provided a number of traits for us
|
||
to use with the <code>derive</code> attribute that can add useful behavior to our custom
|
||
types. Those traits and their behaviors are listed in <a href="../appendix/appendix-03-derivable-traits.html">Appendix C</a><!--
|
||
ignore -->. We’ll cover how to implement these traits with custom behavior as
|
||
well as how to create your own traits in Chapter 10. There are also many
|
||
attributes other than <code>derive</code>; for more information, see <a href="../reference/attributes.html">the “Attributes”
|
||
section of the Rust Reference</a>.</p>
|
||
<p>Our <code>area</code> function is very specific: It only computes the area of rectangles.
|
||
It would be helpful to tie this behavior more closely to our <code>Rectangle</code> struct
|
||
because it won’t work with any other type. Let’s look at how we can continue to
|
||
refactor this code by turning the <code>area</code> function into an <code>area</code> method
|
||
defined on our <code>Rectangle</code> type.</p>
|
||
</body>
|
||
</html>
|