]>
Commit | Line | Data |
---|---|---|
13cf67c4 XL |
1 | ## Method Syntax |
2 | ||
3 | *Methods* are similar to functions: they’re declared with the `fn` keyword and | |
4 | their name, they can have parameters and a return value, and they contain some | |
5 | code that is run when they’re called from somewhere else. However, methods are | |
6 | different from functions in that they’re defined within the context of a struct | |
7 | (or an enum or a trait object, which we cover in Chapters 6 and 17, | |
8 | respectively), and their first parameter is always `self`, which represents the | |
9 | instance of the struct the method is being called on. | |
10 | ||
11 | ### Defining Methods | |
12 | ||
13 | Let’s change the `area` function that has a `Rectangle` instance as a parameter | |
14 | and instead make an `area` method defined on the `Rectangle` struct, as shown | |
69743fb6 | 15 | in Listing 5-13. |
13cf67c4 XL |
16 | |
17 | <span class="filename">Filename: src/main.rs</span> | |
18 | ||
19 | ```rust | |
74b04a01 | 20 | {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-13/src/main.rs}} |
13cf67c4 XL |
21 | ``` |
22 | ||
23 | <span class="caption">Listing 5-13: Defining an `area` method on the | |
24 | `Rectangle` struct</span> | |
25 | ||
26 | To define the function within the context of `Rectangle`, we start an `impl` | |
27 | (implementation) block. Then we move the `area` function within the `impl` | |
28 | curly brackets and change the first (and in this case, only) parameter to be | |
29 | `self` in the signature and everywhere within the body. In `main`, where we | |
30 | called the `area` function and passed `rect1` as an argument, we can instead | |
31 | use *method syntax* to call the `area` method on our `Rectangle` instance. | |
32 | The method syntax goes after an instance: we add a dot followed by the method | |
33 | name, parentheses, and any arguments. | |
34 | ||
35 | In the signature for `area`, we use `&self` instead of `rectangle: &Rectangle` | |
36 | because Rust knows the type of `self` is `Rectangle` due to this method’s being | |
37 | inside the `impl Rectangle` context. Note that we still need to use the `&` | |
38 | before `self`, just as we did in `&Rectangle`. Methods can take ownership of | |
39 | `self`, borrow `self` immutably as we’ve done here, or borrow `self` mutably, | |
40 | just as they can any other parameter. | |
41 | ||
42 | We’ve chosen `&self` here for the same reason we used `&Rectangle` in the | |
43 | function version: we don’t want to take ownership, and we just want to read the | |
44 | data in the struct, not write to it. If we wanted to change the instance that | |
45 | we’ve called the method on as part of what the method does, we’d use `&mut | |
46 | self` as the first parameter. Having a method that takes ownership of the | |
47 | instance by using just `self` as the first parameter is rare; this technique is | |
48 | usually used when the method transforms `self` into something else and you want | |
49 | to prevent the caller from using the original instance after the transformation. | |
50 | ||
51 | The main benefit of using methods instead of functions, in addition to using | |
52 | method syntax and not having to repeat the type of `self` in every method’s | |
53 | signature, is for organization. We’ve put all the things we can do with an | |
54 | instance of a type in one `impl` block rather than making future users of our | |
55 | code search for capabilities of `Rectangle` in various places in the library we | |
56 | provide. | |
57 | ||
58 | > ### Where’s the `->` Operator? | |
59 | > | |
60 | > In C and C++, two different operators are used for calling methods: you use | |
61 | > `.` if you’re calling a method on the object directly and `->` if you’re | |
62 | > calling the method on a pointer to the object and need to dereference the | |
63 | > pointer first. In other words, if `object` is a pointer, | |
64 | > `object->something()` is similar to `(*object).something()`. | |
65 | > | |
66 | > Rust doesn’t have an equivalent to the `->` operator; instead, Rust has a | |
67 | > feature called *automatic referencing and dereferencing*. Calling methods is | |
68 | > one of the few places in Rust that has this behavior. | |
69 | > | |
70 | > Here’s how it works: when you call a method with `object.something()`, Rust | |
71 | > automatically adds in `&`, `&mut`, or `*` so `object` matches the signature of | |
72 | > the method. In other words, the following are the same: | |
73 | > | |
6a06907d | 74 | > <!-- CAN'T EXTRACT SEE BUG https://github.com/rust-lang/mdBook/issues/1127 --> |
13cf67c4 XL |
75 | > ```rust |
76 | > # #[derive(Debug,Copy,Clone)] | |
77 | > # struct Point { | |
78 | > # x: f64, | |
79 | > # y: f64, | |
80 | > # } | |
81 | > # | |
82 | > # impl Point { | |
83 | > # fn distance(&self, other: &Point) -> f64 { | |
84 | > # let x_squared = f64::powi(other.x - self.x, 2); | |
85 | > # let y_squared = f64::powi(other.y - self.y, 2); | |
86 | > # | |
87 | > # f64::sqrt(x_squared + y_squared) | |
88 | > # } | |
89 | > # } | |
90 | > # let p1 = Point { x: 0.0, y: 0.0 }; | |
91 | > # let p2 = Point { x: 5.0, y: 6.5 }; | |
92 | > p1.distance(&p2); | |
93 | > (&p1).distance(&p2); | |
94 | > ``` | |
95 | > | |
96 | > The first one looks much cleaner. This automatic referencing behavior works | |
97 | > because methods have a clear receiver—the type of `self`. Given the receiver | |
98 | > and name of a method, Rust can figure out definitively whether the method is | |
99 | > reading (`&self`), mutating (`&mut self`), or consuming (`self`). The fact | |
100 | > that Rust makes borrowing implicit for method receivers is a big part of | |
101 | > making ownership ergonomic in practice. | |
102 | ||
103 | ### Methods with More Parameters | |
104 | ||
105 | Let’s practice using methods by implementing a second method on the `Rectangle` | |
106 | struct. This time, we want an instance of `Rectangle` to take another instance | |
107 | of `Rectangle` and return `true` if the second `Rectangle` can fit completely | |
108 | within `self`; otherwise it should return `false`. That is, we want to be able | |
109 | to write the program shown in Listing 5-14, once we’ve defined the `can_hold` | |
69743fb6 | 110 | method. |
13cf67c4 XL |
111 | |
112 | <span class="filename">Filename: src/main.rs</span> | |
113 | ||
114 | ```rust,ignore | |
74b04a01 | 115 | {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-14/src/main.rs}} |
13cf67c4 XL |
116 | ``` |
117 | ||
118 | <span class="caption">Listing 5-14: Using the as-yet-unwritten `can_hold` | |
119 | method</span> | |
120 | ||
121 | And the expected output would look like the following, because both dimensions | |
122 | of `rect2` are smaller than the dimensions of `rect1` but `rect3` is wider than | |
123 | `rect1`: | |
124 | ||
125 | ```text | |
126 | Can rect1 hold rect2? true | |
127 | Can rect1 hold rect3? false | |
128 | ``` | |
129 | ||
130 | We know we want to define a method, so it will be within the `impl Rectangle` | |
131 | block. The method name will be `can_hold`, and it will take an immutable borrow | |
132 | of another `Rectangle` as a parameter. We can tell what the type of the | |
133 | parameter will be by looking at the code that calls the method: | |
134 | `rect1.can_hold(&rect2)` passes in `&rect2`, which is an immutable borrow to | |
135 | `rect2`, an instance of `Rectangle`. This makes sense because we only need to | |
136 | read `rect2` (rather than write, which would mean we’d need a mutable borrow), | |
137 | and we want `main` to retain ownership of `rect2` so we can use it again after | |
138 | calling the `can_hold` method. The return value of `can_hold` will be a | |
139 | Boolean, and the implementation will check whether the width and height of | |
140 | `self` are both greater than the width and height of the other `Rectangle`, | |
141 | respectively. Let’s add the new `can_hold` method to the `impl` block from | |
69743fb6 | 142 | Listing 5-13, shown in Listing 5-15. |
13cf67c4 XL |
143 | |
144 | <span class="filename">Filename: src/main.rs</span> | |
145 | ||
146 | ```rust | |
74b04a01 | 147 | {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-15/src/main.rs:here}} |
13cf67c4 XL |
148 | ``` |
149 | ||
150 | <span class="caption">Listing 5-15: Implementing the `can_hold` method on | |
151 | `Rectangle` that takes another `Rectangle` instance as a parameter</span> | |
152 | ||
153 | When we run this code with the `main` function in Listing 5-14, we’ll get our | |
154 | desired output. Methods can take multiple parameters that we add to the | |
155 | signature after the `self` parameter, and those parameters work just like | |
156 | parameters in functions. | |
157 | ||
158 | ### Associated Functions | |
159 | ||
160 | Another useful feature of `impl` blocks is that we’re allowed to define | |
161 | functions within `impl` blocks that *don’t* take `self` as a parameter. These | |
162 | are called *associated functions* because they’re associated with the struct. | |
163 | They’re still functions, not methods, because they don’t have an instance of | |
164 | the struct to work with. You’ve already used the `String::from` associated | |
165 | function. | |
166 | ||
167 | Associated functions are often used for constructors that will return a new | |
168 | instance of the struct. For example, we could provide an associated function | |
169 | that would have one dimension parameter and use that as both width and height, | |
170 | thus making it easier to create a square `Rectangle` rather than having to | |
171 | specify the same value twice: | |
172 | ||
173 | <span class="filename">Filename: src/main.rs</span> | |
174 | ||
175 | ```rust | |
74b04a01 | 176 | {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-03-associated-functions/src/main.rs:here}} |
13cf67c4 XL |
177 | ``` |
178 | ||
179 | To call this associated function, we use the `::` syntax with the struct name; | |
180 | `let sq = Rectangle::square(3);` is an example. This function is namespaced by | |
181 | the struct: the `::` syntax is used for both associated functions and | |
182 | namespaces created by modules. We’ll discuss modules in Chapter 7. | |
183 | ||
184 | ### Multiple `impl` Blocks | |
185 | ||
186 | Each struct is allowed to have multiple `impl` blocks. For example, Listing | |
187 | 5-15 is equivalent to the code shown in Listing 5-16, which has each method | |
69743fb6 | 188 | in its own `impl` block. |
13cf67c4 XL |
189 | |
190 | ```rust | |
74b04a01 | 191 | {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-16/src/main.rs:here}} |
13cf67c4 XL |
192 | ``` |
193 | ||
194 | <span class="caption">Listing 5-16: Rewriting Listing 5-15 using multiple `impl` | |
195 | blocks</span> | |
196 | ||
197 | There’s no reason to separate these methods into multiple `impl` blocks here, | |
198 | but this is valid syntax. We’ll see a case in which multiple `impl` blocks are | |
69743fb6 | 199 | useful in Chapter 10, where we discuss generic types and traits. |
13cf67c4 XL |
200 | |
201 | ## Summary | |
202 | ||
203 | Structs let you create custom types that are meaningful for your domain. By | |
204 | using structs, you can keep associated pieces of data connected to each other | |
205 | and name each piece to make your code clear. Methods let you specify the | |
206 | behavior that instances of your structs have, and associated functions let you | |
207 | namespace functionality that is particular to your struct without having an | |
208 | instance available. | |
209 | ||
210 | But structs aren’t the only way you can create custom types: let’s turn to | |
211 | Rust’s enum feature to add another tool to your toolbox. |