]> git.proxmox.com Git - rustc.git/blame - src/doc/book/second-edition/nostarch/chapter05.md
New upstream version 1.22.1+dfsg1
[rustc.git] / src / doc / book / second-edition / nostarch / chapter05.md
CommitLineData
cc61c64b
XL
1
2[TOC]
3
7cac9316
XL
4# Using Structs to Structure Related Data
5
6A *struct*, or *structure*, is a custom data type that lets us name and package
7together multiple related values that make up a meaningful group. If you’re
8familiar with an object-oriented language, a *struct* is like an object’s data
3b2f2976 9attributes. In this chapter, we’ll compare and contrast tuples with structs,
7cac9316 10demonstrate how to use structs, and discuss how to define methods and
ea8adc8c
XL
11associated functions to specify behavior associated with a struct’s data. The
12struct and *enum* (which is discussed in Chapter 6) concepts are the building
13blocks for creating new types in your program’s domain to take full advantage
14of Rust’s compile time type checking.
7cac9316
XL
15
16## Defining and Instantiating Structs
17
18Structs are similar to tuples, which were discussed in Chapter 3. Like tuples,
19the pieces of a struct can be different types. Unlike tuples, we name each
20piece of data so it’s clear what the values mean. As a result of these names,
21structs are more flexible than tuples: we don’t have to rely on the order of
22the data to specify or access the values of an instance.
23
24To define a struct, we enter the keyword `struct` and name the entire struct. A
25struct’s name should describe the significance of the pieces of data being
26grouped together. Then, inside curly braces, we define the names and types of
27the pieces of data, which we call *fields*. For example, Listing 5-1 shows a
28struct to store information about a user account:
cc61c64b 29
3b2f2976 30```
cc61c64b
XL
31struct User {
32 username: String,
33 email: String,
34 sign_in_count: u64,
35 active: bool,
36}
37```
38
cc61c64b 39Listing 5-1: A `User` struct definition
7cac9316
XL
40
41To use a struct after we’ve defined it, we create an *instance* of that struct
42by specifying concrete values for each of the fields. We create an instance by
43stating the name of the struct, and then add curly braces containing `key:
44value` pairs where the keys are the names of the fields and the values are the
45data we want to store in those fields. We don’t have to specify the fields in
46the same order in which we declared them in the struct. In other words, the
47struct definition is like a general template for the type, and instances fill
48in that template with particular data to create values of the type. For
49example, we can declare a particular user as shown in Listing 5-2:
cc61c64b 50
3b2f2976 51```
cc61c64b
XL
52let user1 = User {
53 email: String::from("someone@example.com"),
54 username: String::from("someusername123"),
55 active: true,
56 sign_in_count: 1,
57};
58```
59
7cac9316
XL
60Listing 5-2: Creating an instance of the `User` struct
61
62To get a specific value from a struct, we can use dot notation. If we wanted
63just this user’s email address, we can use `user1.email` wherever we want to
ea8adc8c
XL
64use this value. If the instance is mutable, we can change a value by using the
65dot notation and assigning into a particular field. Listing 5-3 shows how to
66change the value in the `email` field of a mutable `User` instance:
041b39d2
XL
67
68```
69let mut user1 = User {
70 email: String::from("someone@example.com"),
71 username: String::from("someusername123"),
72 active: true,
73 sign_in_count: 1,
74};
75
76user1.email = String::from("anotheremail@example.com");
77```
78
79Listing 5-3: Changing the value in the `email` field of a `User` instance
7cac9316 80
ea8adc8c
XL
81Note that the entire instance must be mutable; Rust doesn’t allow us to mark
82only certain fields as mutable. Also note that with any expression, we can
83construct a new instance of the struct as the last expression in the function
84body to implicitly return that new instance.
85
86Listing 5-4 shows a `build_user` function that returns a `User` instance with
87the given email and username. The `active` field gets the value of `true`, and
88the `sign_in_count` gets a value of `1`.
7cac9316 89
3b2f2976 90```
7cac9316
XL
91fn build_user(email: String, username: String) -> User {
92 User {
93 email: email,
94 username: username,
95 active: true,
96 sign_in_count: 1,
97 }
98}
99```
100
041b39d2 101Listing 5-4: A `build_user` function that takes an email and username and
7cac9316
XL
102returns a `User` instance
103
ea8adc8c
XL
104It makes sense to name the function arguments with the same name as the struct
105fields, but having to repeat the `email` and `username` field names and
106variables is a bit tedious. If the struct had more fields, repeating each name
107would get even more annoying. Luckily, there's a convenient shorthand!
3b2f2976 108
ea8adc8c 109### Using the Field Init Shorthand when Variables and Fields Have the Same Name
3b2f2976 110
ea8adc8c
XL
111Because the parameter names and the struct field names are exactly the same in
112Listing 5-4, we can use the *field init shorthand* syntax to rewrite
113`build_user` so that it behaves exactly the same but doesn’t have the
114repetition of `email` and `username` in the way shown in Listing 5-5.
3b2f2976
XL
115
116```
7cac9316
XL
117fn build_user(email: String, username: String) -> User {
118 User {
119 email,
120 username,
121 active: true,
122 sign_in_count: 1,
123 }
124}
125```
126
ea8adc8c 127Listing 5-5: A `build_user` function that uses field init shorthand since the
7cac9316
XL
128`email` and `username` parameters have the same name as struct fields
129
ea8adc8c
XL
130Here, we’re creating a new instance of the `User` struct, which has a field
131named `email`. We want to set the `email` field’s value to the value in the
132`email` parameter of the `build_user` function. Because the `email` field and
133the `email` parameter have the same name, we only need to write `email` rather
134than `email: email`.
135
7cac9316
XL
136### Creating Instances From Other Instances With Struct Update Syntax
137
ea8adc8c
XL
138It’s often useful to create a new instance of a struct that uses most of an old
139instance’s values, but changes some. We do this using *struct update syntax*.
140
141First, Listing 5-6 shows how we create a new `User` instance in `user2` without
142the update syntax. We set new values for `email` and `username`, but otherwise
143use the same values from `user1` that we created in Listing 5-2:
7cac9316 144
3b2f2976 145```
7cac9316
XL
146let user2 = User {
147 email: String::from("another@example.com"),
148 username: String::from("anotherusername567"),
149 active: user1.active,
150 sign_in_count: user1.sign_in_count,
151};
152```
153
ea8adc8c
XL
154Listing 5-6: Creating a new `User` instance using some of the values from
155`user1`
cc61c64b 156
ea8adc8c
XL
157Using struct update syntax, we can achieve the same effect with less code,
158shown in Listing 5-7. The syntax `..` specifies that the remaining fields not
159explicitly set should have the same value as the fields in the given instance.
7cac9316 160
3b2f2976 161```
7cac9316
XL
162let user2 = User {
163 email: String::from("another@example.com"),
164 username: String::from("anotherusername567"),
165 ..user1
166};
167```
168
041b39d2 169Listing 5-7: Using struct update syntax to set a new `email` and `username`
7cac9316
XL
170values for a `User` instance but use the rest of the values from the fields of
171the instance in the `user1` variable
172
ea8adc8c
XL
173The code in Listing 5-7 also creates an instance in `user2` that has a
174different value for `email` and `username` but has the same values for the
175`active` and `sign_in_count` fields from `user1`.
176
7cac9316
XL
177### Tuple Structs without Named Fields to Create Different Types
178
179We can also define structs that look similar to tuples, called *tuple structs*,
3b2f2976 180that have the added meaning the struct name provides, but don’t have names
ea8adc8c
XL
181associated with their fields, just the types of the fields. Tuple structs are
182useful when you want to give the whole tuple a name and make the tuple be a
183different type than other tuples, but naming each field as in a regular struct
184would be verbose or redundant.
185
186To define a tuple struct you start with the `struct` keyword and the struct
187name followed by the types in the tuple. For example, here are definitions and
188usages of two tuple structs named `Color` and `Point`:
7cac9316 189
3b2f2976 190```
7cac9316
XL
191struct Color(i32, i32, i32);
192struct Point(i32, i32, i32);
193
194let black = Color(0, 0, 0);
195let origin = Point(0, 0, 0);
196```
197
3b2f2976 198Note that the `black` and `origin` values are different types, since they’re
7cac9316 199instances of different tuple structs. Each struct we define is its own type,
ea8adc8c
XL
200even though the fields within the struct have the same types. For example, a
201function that takes a parameter of type `Color` cannot take a `Point` as an
202argument, even though both types are made up of three `i32` values. Otherwise,
203tuple struct instances behave like tuples, which we covered in Chapter 3: you
204can destructure them into their individual pieces, you can use a `.` followed
205by the index to access an individual value, and so on.
7cac9316
XL
206
207### Unit-Like Structs without Any Fields
208
3b2f2976 209We can also define structs that don’t have any fields! These are called
7cac9316
XL
210*unit-like structs* since they behave similarly to `()`, the unit type.
211Unit-like structs can be useful in situations such as when you need to
3b2f2976
XL
212implement a trait on some type, but you don’t have any data that you want to
213store in the type itself. We’ll be discussing traits in Chapter 10.
7cac9316
XL
214
215PROD: START BOX
216
217### Ownership of Struct Data
cc61c64b 218
ea8adc8c
XL
219In the `User` struct definition in Listing 5-1, we used the owned `String` type
220rather than the `&str` string slice type. This is a deliberate choice because
221we want instances of this struct to own all of its data and for that data to be
222valid for as long as the entire struct is valid.
cc61c64b 223
7cac9316
XL
224It’s possible for structs to store references to data owned by something else,
225but to do so requires the use of *lifetimes*, a Rust feature that is discussed
226in Chapter 10. Lifetimes ensure that the data referenced by a struct is valid
227for as long as the struct is. Let’s say you try to store a reference in a
228struct without specifying lifetimes, like this:
cc61c64b
XL
229
230Filename: src/main.rs
231
3b2f2976 232```
cc61c64b
XL
233struct User {
234 username: &str,
235 email: &str,
236 sign_in_count: u64,
237 active: bool,
238}
239
240fn main() {
241 let user1 = User {
242 email: "someone@example.com",
243 username: "someusername123",
244 active: true,
245 sign_in_count: 1,
246 };
247}
248```
249
250The compiler will complain that it needs lifetime specifiers:
251
3b2f2976 252```
cc61c64b
XL
253error[E0106]: missing lifetime specifier
254 -->
255 |
2562 | username: &str,
257 | ^ expected lifetime parameter
258
259error[E0106]: missing lifetime specifier
260 -->
261 |
2623 | email: &str,
263 | ^ expected lifetime parameter
264```
265
ea8adc8c
XL
266We’ll discuss how to fix these errors so you can store references in structs in
267Chapter 10, but for now, we’ll fix errors like these using owned types like
7cac9316
XL
268`String` instead of references like `&str`.
269
270PROD: END BOX
cc61c64b 271
7cac9316 272## An Example Program Using Structs
cc61c64b
XL
273
274To understand when we might want to use structs, let’s write a program that
7cac9316
XL
275calculates the area of a rectangle. We’ll start with single variables, and then
276refactor the program until we’re using structs instead.
cc61c64b
XL
277
278Let’s make a new binary project with Cargo called *rectangles* that will take
ea8adc8c 279the width and height of a rectangle specified in pixels and will calculate the
041b39d2 280area of the rectangle. Listing 5-8 shows a short program with one way of doing
cc61c64b
XL
281just that in our project’s *src/main.rs*:
282
283Filename: src/main.rs
284
3b2f2976 285```
cc61c64b 286fn main() {
cc61c64b 287 let width1 = 30;
ea8adc8c 288 let height1 = 50;
cc61c64b
XL
289
290 println!(
291 "The area of the rectangle is {} square pixels.",
ea8adc8c 292 area(width1, height1)
cc61c64b
XL
293 );
294}
295
ea8adc8c
XL
296fn area(width: u32, height: u32) -> u32 {
297 width * height
cc61c64b
XL
298}
299```
300
ea8adc8c
XL
301Listing 5-8: Calculating the area of a rectangle specified by its width and
302height in separate variables
cc61c64b 303
7cac9316 304Now, run this program using `cargo run`:
cc61c64b 305
3b2f2976 306```
cc61c64b
XL
307The area of the rectangle is 1500 square pixels.
308```
309
310### Refactoring with Tuples
311
041b39d2 312Even though Listing 5-8 works and figures out the area of the rectangle by
ea8adc8c
XL
313calling the `area` function with each dimension, we can do better. The width
314and the height are related to each other because together they describe one
cc61c64b
XL
315rectangle.
316
317The issue with this method is evident in the signature of `area`:
318
3b2f2976 319```
ea8adc8c 320fn area(width: u32, height: u32) -> u32 {
cc61c64b
XL
321```
322
7cac9316
XL
323The `area` function is supposed to calculate the area of one rectangle, but the
324function we wrote has two parameters. The parameters are related, but that’s
325not expressed anywhere in our program. It would be more readable and more
ea8adc8c 326manageable to group width and height together. We’ve already discussed one way
7cac9316 327we might do that in the Grouping Values into Tuples section of Chapter 3 on
041b39d2 328page XX: by using tuples. Listing 5-9 shows another version of our program that
7cac9316 329uses tuples:
cc61c64b
XL
330
331Filename: src/main.rs
332
3b2f2976 333```
cc61c64b 334fn main() {
ea8adc8c 335 let rect1 = (30, 50);
cc61c64b
XL
336
337 println!(
338 "The area of the rectangle is {} square pixels.",
339 area(rect1)
340 );
341}
342
343fn area(dimensions: (u32, u32)) -> u32 {
344 dimensions.0 * dimensions.1
345}
346```
347
ea8adc8c 348Listing 5-8: Specifying the width and height of the rectangle with a tuple
cc61c64b 349
7cac9316
XL
350In one way, this program is better. Tuples let us add a bit of structure, and
351we’re now passing just one argument. But in another way this version is less
352clear: tuples don’t name their elements, so our calculation has become more
353confusing because we have to index into the parts of the tuple.
cc61c64b 354
ea8adc8c 355It doesn’t matter if we mix up width and height for the area calculation, but
7cac9316 356if we want to draw the rectangle on the screen, it would matter! We would have
ea8adc8c 357to keep in mind that `width` is the tuple index `0` and `height` is the tuple
7cac9316
XL
358index `1`. If someone else worked on this code, they would have to figure this
359out and keep it in mind as well. It would be easy to forget or mix up these
360values and cause errors, because we haven’t conveyed the meaning of our data in
361our code.
cc61c64b
XL
362
363### Refactoring with Structs: Adding More Meaning
364
7cac9316
XL
365We use structs to add meaning by labeling the data. We can transform the tuple
366we’re using into a data type with a name for the whole as well as names for the
041b39d2 367parts, as shown in Listing 5-10:
cc61c64b
XL
368
369Filename: src/main.rs
370
3b2f2976 371```
cc61c64b 372struct Rectangle {
cc61c64b 373 width: u32,
ea8adc8c 374 height: u32,
cc61c64b
XL
375}
376
377fn main() {
ea8adc8c 378 let rect1 = Rectangle { width: 30, height: 50 };
cc61c64b
XL
379
380 println!(
381 "The area of the rectangle is {} square pixels.",
382 area(&rect1)
383 );
384}
385
386fn area(rectangle: &Rectangle) -> u32 {
ea8adc8c 387 rectangle.width * rectangle.height
cc61c64b
XL
388}
389```
390
041b39d2 391Listing 5-10: Defining a `Rectangle` struct
cc61c64b 392
7cac9316 393Here we’ve defined a struct and named it `Rectangle`. Inside the `{}` we
ea8adc8c
XL
394defined the fields as `width` and `height`, both of which have type `u32`. Then
395in `main` we create a particular instance of a `Rectangle` that has a width of
39630 and a height of 50.
cc61c64b 397
7cac9316
XL
398Our `area` function is now defined with one parameter, which we’ve named
399`rectangle`, whose type is an immutable borrow of a struct `Rectangle`
400instance. As mentioned in Chapter 4, we want to borrow the struct rather than
401take ownership of it. This way, `main` retains its ownership and can continue
402using `rect1`, which is the reason we use the `&` in the function signature and
403where we call the function.
cc61c64b 404
ea8adc8c 405The `area` function accesses the `width` and `height` fields of the `Rectangle`
3b2f2976 406instance. Our function signature for `area` now indicates exactly what we mean:
ea8adc8c
XL
407calculate the area of a `Rectangle` using its `width` and `height` fields. This
408conveys that the width and height are related to each other, and gives
3b2f2976
XL
409descriptive names to the values rather than using the tuple index values of `0`
410and `1`—a win for clarity.
cc61c64b
XL
411
412### Adding Useful Functionality with Derived Traits
413
7cac9316
XL
414It would be helpful to be able to print out an instance of the `Rectangle`
415while we’re debugging our program in order to see the values for all its
041b39d2 416fields. Listing 5-11 uses the `println!` macro as we have been in earlier
7cac9316 417chapters:
cc61c64b
XL
418
419Filename: src/main.rs
420
3b2f2976 421```
cc61c64b 422struct Rectangle {
cc61c64b 423 width: u32,
ea8adc8c 424 height: u32,
cc61c64b
XL
425}
426
427fn main() {
ea8adc8c 428 let rect1 = Rectangle { width: 30, height: 50 };
cc61c64b
XL
429
430 println!("rect1 is {}", rect1);
431}
432```
433
041b39d2 434Listing 5-11: Attempting to print a `Rectangle` instance
cc61c64b 435
7cac9316 436When we run this code, we get an error with this core message:
cc61c64b 437
3b2f2976 438```
cc61c64b
XL
439error[E0277]: the trait bound `Rectangle: std::fmt::Display` is not satisfied
440```
441
442The `println!` macro can do many kinds of formatting, and by default, `{}`
443tells `println!` to use formatting known as `Display`: output intended for
7cac9316
XL
444direct end user consumption. The primitive types we’ve seen so far implement
445`Display` by default, because there’s only one way you’d want to show a `1` or
446any other primitive type to a user. But with structs, the way `println!` should
447format the output is less clear because there are more display possibilities:
448do you want commas or not? Do you want to print the curly braces? Should all
449the fields be shown? Due to this ambiguity, Rust doesn’t try to guess what we
450want and structs don’t have a provided implementation of `Display`.
cc61c64b 451
7cac9316 452If we continue reading the errors, we’ll find this helpful note:
cc61c64b 453
3b2f2976 454```
cc61c64b
XL
455note: `Rectangle` cannot be formatted with the default formatter; try using
456`:?` instead if you are using a format string
457```
458
7cac9316
XL
459Let’s try it! The `println!` macro call will now look like `println!("rect1 is
460{:?}", rect1);`. Putting the specifier `:?` inside the `{}` tells `println!` we
461want to use an output format called `Debug`. `Debug` is a trait that enables us
462to print out our struct in a way that is useful for developers so we can see
463its value while we’re debugging our code.
cc61c64b 464
7cac9316 465Run the code with this change. Drat! We still get an error:
cc61c64b 466
3b2f2976 467```
cc61c64b
XL
468error: the trait bound `Rectangle: std::fmt::Debug` is not satisfied
469```
470
7cac9316 471But again, the compiler gives us a helpful note:
cc61c64b 472
3b2f2976 473```
cc61c64b
XL
474note: `Rectangle` cannot be formatted using `:?`; if it is defined in your
475crate, add `#[derive(Debug)]` or manually implement it
476```
477
478Rust *does* include functionality to print out debugging information, but we
7cac9316
XL
479have to explicitly opt-in to make that functionality available for our struct.
480To do that, we add the annotation `#[derive(Debug)]` just before the struct
041b39d2 481definition, as shown in Listing 5-12:
7cac9316
XL
482
483Filename: src/main.rs
cc61c64b 484
3b2f2976 485```
cc61c64b
XL
486#[derive(Debug)]
487struct Rectangle {
cc61c64b 488 width: u32,
ea8adc8c 489 height: u32,
cc61c64b
XL
490}
491
492fn main() {
ea8adc8c 493 let rect1 = Rectangle { width: 30, height: 50 };
cc61c64b
XL
494
495 println!("rect1 is {:?}", rect1);
496}
497```
498
041b39d2 499Listing 5-12: Adding the annotation to derive the `Debug` trait and printing
7cac9316 500the `Rectangle` instance using debug formatting
cc61c64b 501
7cac9316
XL
502Now when we run the program, we won’t get any errors and we’ll see the
503following output:
cc61c64b 504
3b2f2976 505```
ea8adc8c 506rect1 is Rectangle { width: 30, height: 50 }
cc61c64b
XL
507```
508
509Nice! It’s not the prettiest output, but it shows the values of all the fields
7cac9316
XL
510for this instance, which would definitely help during debugging. When we have
511larger structs, it’s useful to have output that’s a bit easier to read; in
512those cases, we can use `{:#?}` instead of `{:?}` in the `println!` string.
513When we use the `{:#?}` style in the example, the output will look like this:
cc61c64b 514
3b2f2976 515```
cc61c64b 516rect1 is Rectangle {
ea8adc8c
XL
517 width: 30,
518 height: 50
cc61c64b
XL
519}
520```
521
7cac9316
XL
522Rust has provided a number of traits for us to use with the `derive` annotation
523that can add useful behavior to our custom types. Those traits and their
524behaviors are listed in Appendix C. We’ll cover how to implement these traits
525with custom behavior as well as how to create your own traits in Chapter 10.
cc61c64b 526
7cac9316
XL
527Our `area` function is very specific: it only computes the area of rectangles.
528It would be helpful to tie this behavior more closely to our `Rectangle`
3b2f2976 529struct, because it won’t work with any other type. Let’s look at how we can
7cac9316
XL
530continue to refactor this code by turning the `area` function into an `area`
531*method* defined on our `Rectangle` type.
cc61c64b
XL
532
533## Method Syntax
534
535*Methods* are similar to functions: they’re declared with the `fn` keyword and
041b39d2 536their name, they can have parameters and a return value, and they contain some
7cac9316
XL
537code that is run when they’re called from somewhere else. However, methods are
538different from functions in that they’re defined within the context of a struct
539(or an enum or a trait object, which we cover in Chapters 6 and 17,
540respectively), and their first parameter is always `self`, which represents the
541instance of the struct the method is being called on.
cc61c64b
XL
542
543### Defining Methods
544
7cac9316
XL
545Let’s change the `area` function that has a `Rectangle` instance as a parameter
546and instead make an `area` method defined on the `Rectangle` struct, as shown
041b39d2 547in Listing 5-13:
cc61c64b
XL
548
549Filename: src/main.rs
550
3b2f2976 551```
cc61c64b
XL
552#[derive(Debug)]
553struct Rectangle {
cc61c64b 554 width: u32,
ea8adc8c 555 height: u32,
cc61c64b
XL
556}
557
558impl Rectangle {
559 fn area(&self) -> u32 {
ea8adc8c 560 self.width * self.height
cc61c64b
XL
561 }
562}
563
564fn main() {
ea8adc8c 565 let rect1 = Rectangle { width: 30, height: 50 };
cc61c64b
XL
566
567 println!(
568 "The area of the rectangle is {} square pixels.",
569 rect1.area()
570 );
571}
572```
573
041b39d2 574Listing 5-13: Defining an `area` method on the `Rectangle` struct
cc61c64b 575
7cac9316
XL
576To define the function within the context of `Rectangle`, we start an `impl`
577(*implementation*) block. Then we move the `area` function within the `impl`
578curly braces and change the first (and in this case, only) parameter to be
579`self` in the signature and everywhere within the body. In `main` where we
580called the `area` function and passed `rect1` as an argument, we can instead
581use *method syntax* to call the `area` method on our `Rectangle` instance.
582The method syntax goes after an instance: we add a dot followed by the method
583name, parentheses, and any arguments.
cc61c64b 584
7cac9316
XL
585In the signature for `area`, we use `&self` instead of `rectangle: &Rectangle`
586because Rust knows the type of `self` is `Rectangle` due to this method being
587inside the `impl Rectangle` context. Note that we still need to use the `&`
588before `self`, just like we did in `&Rectangle`. Methods can take ownership of
589`self`, borrow `self` immutably as we’ve done here, or borrow `self` mutably,
590just like any other parameter.
cc61c64b
XL
591
592We’ve chosen `&self` here for the same reason we used `&Rectangle` in the
7cac9316
XL
593function version: we don’t want to take ownership, and we just want to read the
594data in the struct, not write to it. If we wanted to change the instance that
595we’ve called the method on as part of what the method does, we’d use `&mut
596self` as the first parameter. Having a method that takes ownership of the
597instance by using just `self` as the first parameter is rare; this technique is
598usually used when the method transforms `self` into something else and we want
599to prevent the caller from using the original instance after the transformation.
600
601The main benefit of using methods instead of functions, in addition to using
cc61c64b
XL
602method syntax and not having to repeat the type of `self` in every method’s
603signature, is for organization. We’ve put all the things we can do with an
7cac9316
XL
604instance of a type in one `impl` block rather than making future users of our
605code search for capabilities of `Rectangle` in various places in the library we
606provide.
cc61c64b
XL
607
608PROD: START BOX
609
610### Where’s the `->` Operator?
611
7cac9316
XL
612In languages like C++, two different operators are used for calling methods:
613you use `.` if you’re calling a method on the object directly and `->` if
614you’re calling the method on a pointer to the object and need to dereference
615the pointer first. In other words, if `object` is a pointer,
616`object->something()` is similar to `(*object).something()`.
cc61c64b
XL
617
618Rust doesn’t have an equivalent to the `->` operator; instead, Rust has a
619feature called *automatic referencing and dereferencing*. Calling methods is
7cac9316 620one of the few places in Rust that has this behavior.
cc61c64b
XL
621
622Here’s how it works: when you call a method with `object.something()`, Rust
7cac9316
XL
623automatically adds in `&`, `&mut`, or `*` so `object` matches the signature of
624the method. In other words, the following are the same:
cc61c64b 625
3b2f2976 626```
cc61c64b
XL
627p1.distance(&p2);
628(&p1).distance(&p2);
629```
630
7cac9316
XL
631The first one looks much cleaner. This automatic referencing behavior works
632because methods have a clear receiver—the type of `self`. Given the receiver
633and name of a method, Rust can figure out definitively whether the method is
3b2f2976
XL
634reading (`&self`), mutating (`&mut self`), or consuming (`self`). The fact
635that Rust makes borrowing implicit for method receivers is a big part of
636making ownership ergonomic in practice.
cc61c64b
XL
637
638PROD: END BOX
639
7cac9316 640### Methods with More Parameters
cc61c64b 641
7cac9316
XL
642Let’s practice using methods by implementing a second method on the `Rectangle`
643struct. This time, we want an instance of `Rectangle` to take another instance
644of `Rectangle` and return `true` if the second `Rectangle` can fit completely
645within `self`; otherwise it should return `false`. That is, we want to be able
041b39d2 646to write the program shown in Listing 5-14, once we’ve defined the `can_hold`
7cac9316 647method:
cc61c64b
XL
648
649Filename: src/main.rs
650
3b2f2976 651```
cc61c64b 652fn main() {
ea8adc8c
XL
653 let rect1 = Rectangle { width: 30, height: 50 };
654 let rect2 = Rectangle { width: 10, height: 40 };
655 let rect3 = Rectangle { width: 60, height: 45 };
cc61c64b
XL
656
657 println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
658 println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
659}
660```
661
041b39d2 662Listing 5-14: Demonstration of using the as-yet-unwritten `can_hold` method
cc61c64b 663
7cac9316
XL
664And the expected output would look like the following, because both dimensions
665of `rect2` are smaller than the dimensions of `rect1`, but `rect3` is wider
666than `rect1`:
cc61c64b 667
3b2f2976 668```
cc61c64b
XL
669Can rect1 hold rect2? true
670Can rect1 hold rect3? false
671```
672
673We know we want to define a method, so it will be within the `impl Rectangle`
674block. The method name will be `can_hold`, and it will take an immutable borrow
7cac9316
XL
675of another `Rectangle` as a parameter. We can tell what the type of the
676parameter will be by looking at the code that calls the method:
677`rect1.can_hold(&rect2)` passes in `&rect2`, which is an immutable borrow to
678`rect2`, an instance of `Rectangle`. This makes sense because we only need to
679read `rect2` (rather than write, which would mean we’d need a mutable borrow),
680and we want `main` to retain ownership of `rect2` so we can use it again after
681calling the `can_hold` method. The return value of `can_hold` will be a
ea8adc8c
XL
682boolean, and the implementation will check whether the width and height of
683`self` are both greater than the width and height of the other `Rectangle`,
7cac9316 684respectively. Let’s add the new `can_hold` method to the `impl` block from
041b39d2 685Listing 5-13, shown in Listing 5-15:
cc61c64b
XL
686
687Filename: src/main.rs
688
3b2f2976 689```
cc61c64b
XL
690impl Rectangle {
691 fn area(&self) -> u32 {
ea8adc8c 692 self.width * self.height
cc61c64b
XL
693 }
694
695 fn can_hold(&self, other: &Rectangle) -> bool {
ea8adc8c 696 self.width > other.width && self.height > other.height
cc61c64b
XL
697 }
698}
699```
700
041b39d2 701Listing 5-15: Implementing the `can_hold` method on `Rectangle` that takes
7cac9316 702another `Rectangle` instance as a parameter
cc61c64b 703
041b39d2 704When we run this code with the `main` function in Listing 5-14, we’ll get our
7cac9316
XL
705desired output. Methods can take multiple parameters that we add to the
706signature after the `self` parameter, and those parameters work just like
707parameters in functions.
cc61c64b
XL
708
709### Associated Functions
710
7cac9316
XL
711Another useful feature of `impl` blocks is that we’re allowed to define
712functions within `impl` blocks that *don’t* take `self` as a parameter. These
713are called *associated functions* because they’re associated with the struct.
714They’re still functions, not methods, because they don’t have an instance of
715the struct to work with. You’ve already used the `String::from` associated
716function.
cc61c64b
XL
717
718Associated functions are often used for constructors that will return a new
719instance of the struct. For example, we could provide an associated function
ea8adc8c 720that would have one dimension parameter and use that as both width and height,
cc61c64b
XL
721thus making it easier to create a square `Rectangle` rather than having to
722specify the same value twice:
723
724Filename: src/main.rs
725
3b2f2976 726```
cc61c64b
XL
727impl Rectangle {
728 fn square(size: u32) -> Rectangle {
ea8adc8c 729 Rectangle { width: size, height: size }
cc61c64b
XL
730 }
731}
732```
733
7cac9316 734To call this associated function, we use the `::` syntax with the struct name,
3b2f2976
XL
735like `let sq = Rectangle::square(3);`, for example. This function is
736namespaced by the struct: the `::` syntax is used for both associated functions
737and namespaces created by modules, which we’ll discuss in Chapter 7.
738
739### Multiple `impl` Blocks
740
741Each struct is allowed to have multiple `impl` blocks. For example, Listing
7425-15 is equivalent to the code shown in Listing 5-16, which has each method
743in its own `impl` block:
744
745```
746impl Rectangle {
747 fn area(&self) -> u32 {
ea8adc8c 748 self.width * self.height
3b2f2976
XL
749 }
750}
751
752impl Rectangle {
753 fn can_hold(&self, other: &Rectangle) -> bool {
ea8adc8c 754 self.width > other.width && self.height > other.height
3b2f2976
XL
755 }
756}
757```
758
759Listing 5-16: Rewriting Listing 5-15 using multiple `impl` blocks
760
761There’s no reason to separate these methods into multiple `impl` blocks here,
762but it’s valid syntax. We will see a case when multiple `impl` blocks are useful
763in Chapter 10 when we discuss generic types and traits.
cc61c64b
XL
764
765## Summary
766
767Structs let us create custom types that are meaningful for our domain. By using
768structs, we can keep associated pieces of data connected to each other and name
769each piece to make our code clear. Methods let us specify the behavior that
770instances of our structs have, and associated functions let us namespace
771functionality that is particular to our struct without having an instance
772available.
773
7cac9316
XL
774But structs aren’t the only way we can create custom types: let’s turn to
775Rust’s enum feature to add another tool to our toolbox.