Files
docs-rust/ch05/ch05-02-example-structs.html
2026-06-22 21:27:36 +05:30

300 lines
16 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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, lets write a program that
calculates the area of a rectangle. Well start by using single variables and
then refactor the program until were using structs instead.</p>
<p>Lets 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 projects <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) -&gt; 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) -&gt; 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 its 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. Weve 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)) -&gt; 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
were now passing just one argument. But in another way, this version is less
clear: Tuples dont 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 wouldnt 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 havent conveyed the meaning of
our data in our code, its 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
were 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(&amp;rect1)
);
}
fn area(rectangle: &amp;Rectangle) -&gt; 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, weve 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 weve 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>&amp;</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>Itd be useful to be able to print an instance of <code>Rectangle</code> while were
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 wont 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 weve seen so far
implement <code>Display</code> by default because theres only one way youd 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
doesnt try to guess what we want, and structs dont 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, well 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>Lets 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 were 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 wont get any errors, and well 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! Its 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, its useful to have output thats 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>). Well 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>Heres an example where were 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!(&amp;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 expressions value, the <code>width</code> field will get the
same value as if we didnt have the <code>dbg!</code> call there. We dont want <code>dbg!</code> to
take ownership of <code>rect1</code>, so we use a reference to <code>rect1</code> in the next call.
Heres 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] &amp;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 were
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>&amp;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 youre 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 -->. Well 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 wont work with any other type. Lets 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>