]> git.proxmox.com Git - rustc.git/blob - src/doc/book/src/ch05-02-example-structs.md
New upstream version 1.63.0+dfsg1
[rustc.git] / src / doc / book / src / ch05-02-example-structs.md
1 ## An Example Program Using Structs
2
3 To understand when we might want to use structs, let’s write a program that
4 calculates the area of a rectangle. We’ll start by using single variables, and
5 then refactor the program until we’re using structs instead.
6
7 Let’s make a new binary project with Cargo called *rectangles* that will take
8 the width and height of a rectangle specified in pixels and calculate the area
9 of the rectangle. Listing 5-8 shows a short program with one way of doing
10 exactly that in our project’s *src/main.rs*.
11
12 <span class="filename">Filename: src/main.rs</span>
13
14 ```rust
15 {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-08/src/main.rs:all}}
16 ```
17
18 <span class="caption">Listing 5-8: Calculating the area of a rectangle
19 specified by separate width and height variables</span>
20
21 Now, run this program using `cargo run`:
22
23 ```console
24 {{#include ../listings/ch05-using-structs-to-structure-related-data/listing-05-08/output.txt}}
25 ```
26
27 This code succeeds in figuring out the area of the rectangle by calling the
28 `area` function with each dimension, but we can do more to make this code clear
29 and readable.
30
31 The issue with this code is evident in the signature of `area`:
32
33 ```rust,ignore
34 {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-08/src/main.rs:here}}
35 ```
36
37 The `area` function is supposed to calculate the area of one rectangle, but the
38 function we wrote has two parameters, and it’s not clear anywhere in our
39 program that the parameters are related. It would be more readable and more
40 manageable to group width and height together. We’ve already discussed one way
41 we might do that in [“The Tuple Type”][the-tuple-type]<!-- ignore --> section
42 of Chapter 3: by using tuples.
43
44 ### Refactoring with Tuples
45
46 Listing 5-9 shows another version of our program that uses tuples.
47
48 <span class="filename">Filename: src/main.rs</span>
49
50 ```rust
51 {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-09/src/main.rs}}
52 ```
53
54 <span class="caption">Listing 5-9: Specifying the width and height of the
55 rectangle with a tuple</span>
56
57 In one way, this program is better. Tuples let us add a bit of structure, and
58 we’re now passing just one argument. But in another way, this version is less
59 clear: tuples don’t name their elements, so we have to index into the parts of
60 the tuple, making our calculation less obvious.
61
62 Mixing up the width and height wouldn’t matter for the area calculation, but if
63 we want to draw the rectangle on the screen, it would matter! We would have to
64 keep in mind that `width` is the tuple index `0` and `height` is the tuple
65 index `1`. This would be even harder for someone else to figure out and keep in
66 mind if they were to use our code. Because we haven’t conveyed the meaning of
67 our data in our code, it’s now easier to introduce errors.
68
69 ### Refactoring with Structs: Adding More Meaning
70
71 We use structs to add meaning by labeling the data. We can transform the tuple
72 we’re using into a struct with a name for the whole as well as names for the
73 parts, as shown in Listing 5-10.
74
75 <span class="filename">Filename: src/main.rs</span>
76
77 ```rust
78 {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-10/src/main.rs}}
79 ```
80
81 <span class="caption">Listing 5-10: Defining a `Rectangle` struct</span>
82
83 Here we’ve defined a struct and named it `Rectangle`. Inside the curly
84 brackets, we defined the fields as `width` and `height`, both of which have
85 type `u32`. Then in `main`, we created a particular instance of `Rectangle`
86 that has a width of 30 and a height of 50.
87
88 Our `area` function is now defined with one parameter, which we’ve named
89 `rectangle`, whose type is an immutable borrow of a struct `Rectangle`
90 instance. As mentioned in Chapter 4, we want to borrow the struct rather than
91 take ownership of it. This way, `main` retains its ownership and can continue
92 using `rect1`, which is the reason we use the `&` in the function signature and
93 where we call the function.
94
95 The `area` function accesses the `width` and `height` fields of the `Rectangle`
96 instance (note that accessing fields of a borrowed struct instance does not
97 move the field values, which is why you often see borrows of structs). Our
98 function signature for `area` now says exactly what we mean: calculate the area
99 of `Rectangle`, using its `width` and `height` fields. This conveys that the
100 width and height are related to each other, and it gives descriptive names to
101 the values rather than using the tuple index values of `0` and `1`. This is a
102 win for clarity.
103
104 ### Adding Useful Functionality with Derived Traits
105
106 It’d be useful to be able to print an instance of `Rectangle` while we’re
107 debugging our program and see the values for all its fields. Listing 5-11 tries
108 using the [`println!` macro][println]<!-- ignore --> as we have used in
109 previous chapters. This won’t work, however.
110
111 <span class="filename">Filename: src/main.rs</span>
112
113 ```rust,ignore,does_not_compile
114 {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-11/src/main.rs}}
115 ```
116
117 <span class="caption">Listing 5-11: Attempting to print a `Rectangle`
118 instance</span>
119
120 When we compile this code, we get an error with this core message:
121
122 ```text
123 {{#include ../listings/ch05-using-structs-to-structure-related-data/listing-05-11/output.txt:3}}
124 ```
125
126 The `println!` macro can do many kinds of formatting, and by default, the curly
127 brackets tell `println!` to use formatting known as `Display`: output intended
128 for direct end user consumption. The primitive types we’ve seen so far
129 implement `Display` by default, because there’s only one way you’d want to show
130 a `1` or any other primitive type to a user. But with structs, the way
131 `println!` should format the output is less clear because there are more
132 display possibilities: Do you want commas or not? Do you want to print the
133 curly brackets? Should all the fields be shown? Due to this ambiguity, Rust
134 doesn’t try to guess what we want, and structs don’t have a provided
135 implementation of `Display` to use with `println!` and the `{}` placeholder.
136
137 If we continue reading the errors, we’ll find this helpful note:
138
139 ```text
140 {{#include ../listings/ch05-using-structs-to-structure-related-data/listing-05-11/output.txt:9:10}}
141 ```
142
143 Let’s try it! The `println!` macro call will now look like `println!("rect1 is
144 {:?}", rect1);`. Putting the specifier `:?` inside the curly brackets tells
145 `println!` we want to use an output format called `Debug`. The `Debug` trait
146 enables us to print our struct in a way that is useful for developers so we can
147 see its value while we’re debugging our code.
148
149 Compile the code with this change. Drat! We still get an error:
150
151 ```text
152 {{#include ../listings/ch05-using-structs-to-structure-related-data/output-only-01-debug/output.txt:3}}
153 ```
154
155 But again, the compiler gives us a helpful note:
156
157 ```text
158 {{#include ../listings/ch05-using-structs-to-structure-related-data/output-only-01-debug/output.txt:9:10}}
159 ```
160
161 Rust *does* include functionality to print out debugging information, but we
162 have to explicitly opt in to make that functionality available for our struct.
163 To do that, we add the outer attribute `#[derive(Debug)]` just before the
164 struct definition, as shown in Listing 5-12.
165
166 <span class="filename">Filename: src/main.rs</span>
167
168 ```rust
169 {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-12/src/main.rs}}
170 ```
171
172 <span class="caption">Listing 5-12: Adding the attribute to derive the `Debug`
173 trait and printing the `Rectangle` instance using debug formatting</span>
174
175 Now when we run the program, we won’t get any errors, and we’ll see the
176 following output:
177
178 ```console
179 {{#include ../listings/ch05-using-structs-to-structure-related-data/listing-05-12/output.txt}}
180 ```
181
182 Nice! It’s not the prettiest output, but it shows the values of all the fields
183 for this instance, which would definitely help during debugging. When we have
184 larger structs, it’s useful to have output that’s a bit easier to read; in
185 those cases, we can use `{:#?}` instead of `{:?}` in the `println!` string.
186 In this example, using the `{:#?}` style will output:
187
188 ```console
189 {{#include ../listings/ch05-using-structs-to-structure-related-data/output-only-02-pretty-debug/output.txt}}
190 ```
191
192 Another way to print out a value using the `Debug` format is to use the [`dbg!`
193 macro][dbg]<!-- ignore -->, which takes ownership of an expression (as opposed
194 to `println!` that takes a reference), prints the file and line number of where
195 that `dbg!` macro call occurs in your code along with the resulting value of
196 that expression, and returns ownership of the value.
197
198 > Note: Calling the `dbg!` macro prints to the standard error console stream
199 > (`stderr`), as opposed to `println!` which prints to the standard output
200 > console stream (`stdout`). We’ll talk more about `stderr` and `stdout` in the
201 > “[“Writing Error Messages to Standard Error Instead of Standard
202 > Output” section in Chapter 12][err]<!-- ignore -->.
203
204 Here’s an example where we’re interested in the value that gets assigned to the
205 `width` field, as well as the value of the whole struct in `rect1`:
206
207 ```rust
208 {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-05-dbg-macro/src/main.rs}}
209 ```
210
211 We can put `dbg!` around the expression `30 * scale` and, because `dbg!`
212 returns ownership of the expression’s value, the `width` field will get the
213 same value as if we didn’t have the `dbg!` call there. We don’t want `dbg!` to
214 take ownership of `rect1`, so we use a reference to `rect1` in the next call.
215 Here’s what the output of this example looks like:
216
217 ```console
218 {{#include ../listings/ch05-using-structs-to-structure-related-data/no-listing-05-dbg-macro/output.txt}}
219 ```
220
221 We can see the first bit of output came from *src/main.rs* line 10, where we’re
222 debugging the expression `30 * scale`, and its resulting value is 60 (the
223 `Debug` formatting implemented for integers is to print only their value). The
224 `dbg!` call on line 14 of *src/main.rs* outputs the value of `&rect1`, which is
225 the `Rectangle` struct. This output uses the pretty `Debug` formatting of the
226 `Rectangle` type. The `dbg!` macro can be really helpful when you’re trying to
227 figure out what your code is doing!
228
229 In addition to the `Debug` trait, Rust has provided a number of traits for us
230 to use with the `derive` attribute that can add useful behavior to our custom
231 types. Those traits and their behaviors are listed in [Appendix C][app-c]<!--
232 ignore -->. We’ll cover how to implement these traits with custom behavior as
233 well as how to create your own traits in Chapter 10. There are also many
234 attributes other than `derive`; for more information, see [the “Attributes”
235 section of the Rust Reference][attributes].
236
237 Our `area` function is very specific: it only computes the area of rectangles.
238 It would be helpful to tie this behavior more closely to our `Rectangle`
239 struct, because it won’t work with any other type. Let’s look at how we can
240 continue to refactor this code by turning the `area` function into an `area`
241 *method* defined on our `Rectangle` type.
242
243 [the-tuple-type]: ch03-02-data-types.html#the-tuple-type
244 [app-c]: appendix-03-derivable-traits.md
245 [println]: ../std/macro.println.html
246 [dbg]: ../std/macro.dbg.html
247 [err]: ch12-06-writing-to-stderr-instead-of-stdout.html
248 [attributes]: ../reference/attributes.html