]> git.lizzy.rs Git - rust.git/blob - src/doc/trpl/traits.md
Rollup merge of #25614 - parir:patch-2, r=alexcrichton
[rust.git] / src / doc / trpl / traits.md
1 % Traits
2
3 Do you remember the `impl` keyword, used to call a function with [method
4 syntax][methodsyntax]?
5
6 ```rust
7 struct Circle {
8     x: f64,
9     y: f64,
10     radius: f64,
11 }
12
13 impl Circle {
14     fn area(&self) -> f64 {
15         std::f64::consts::PI * (self.radius * self.radius)
16     }
17 }
18 ```
19
20 [methodsyntax]: method-syntax.html
21
22 Traits are similar, except that we define a trait with just the method
23 signature, then implement the trait for that struct. Like this:
24
25 ```rust
26 struct Circle {
27     x: f64,
28     y: f64,
29     radius: f64,
30 }
31
32 trait HasArea {
33     fn area(&self) -> f64;
34 }
35
36 impl HasArea for Circle {
37     fn area(&self) -> f64 {
38         std::f64::consts::PI * (self.radius * self.radius)
39     }
40 }
41 ```
42
43 As you can see, the `trait` block looks very similar to the `impl` block,
44 but we don’t define a body, just a type signature. When we `impl` a trait,
45 we use `impl Trait for Item`, rather than just `impl Item`.
46
47 We can use traits to constrain our generics. Consider this function, which
48 does not compile, and gives us a similar error:
49
50 ```rust,ignore
51 fn print_area<T>(shape: T) {
52     println!("This shape has an area of {}", shape.area());
53 }
54 ```
55
56 Rust complains:
57
58 ```text
59 error: type `T` does not implement any method in scope named `area`
60 ```
61
62 Because `T` can be any type, we can’t be sure that it implements the `area`
63 method. But we can add a ‘trait constraint’ to our generic `T`, ensuring
64 that it does:
65
66 ```rust
67 # trait HasArea {
68 #     fn area(&self) -> f64;
69 # }
70 fn print_area<T: HasArea>(shape: T) {
71     println!("This shape has an area of {}", shape.area());
72 }
73 ```
74
75 The syntax `<T: HasArea>` means `any type that implements the HasArea trait`.
76 Because traits define function type signatures, we can be sure that any type
77 which implements `HasArea` will have an `.area()` method.
78
79 Here’s an extended example of how this works:
80
81 ```rust
82 trait HasArea {
83     fn area(&self) -> f64;
84 }
85
86 struct Circle {
87     x: f64,
88     y: f64,
89     radius: f64,
90 }
91
92 impl HasArea for Circle {
93     fn area(&self) -> f64 {
94         std::f64::consts::PI * (self.radius * self.radius)
95     }
96 }
97
98 struct Square {
99     x: f64,
100     y: f64,
101     side: f64,
102 }
103
104 impl HasArea for Square {
105     fn area(&self) -> f64 {
106         self.side * self.side
107     }
108 }
109
110 fn print_area<T: HasArea>(shape: T) {
111     println!("This shape has an area of {}", shape.area());
112 }
113
114 fn main() {
115     let c = Circle {
116         x: 0.0f64,
117         y: 0.0f64,
118         radius: 1.0f64,
119     };
120
121     let s = Square {
122         x: 0.0f64,
123         y: 0.0f64,
124         side: 1.0f64,
125     };
126
127     print_area(c);
128     print_area(s);
129 }
130 ```
131
132 This program outputs:
133
134 ```text
135 This shape has an area of 3.141593
136 This shape has an area of 1
137 ```
138
139 As you can see, `print_area` is now generic, but also ensures that we have
140 passed in the correct types. If we pass in an incorrect type:
141
142 ```rust,ignore
143 print_area(5);
144 ```
145
146 We get a compile-time error:
147
148 ```text
149 error: failed to find an implementation of trait main::HasArea for int
150 ```
151
152 So far, we’ve only added trait implementations to structs, but you can
153 implement a trait for any type. So technically, we _could_ implement `HasArea`
154 for `i32`:
155
156 ```rust
157 trait HasArea {
158     fn area(&self) -> f64;
159 }
160
161 impl HasArea for i32 {
162     fn area(&self) -> f64 {
163         println!("this is silly");
164
165         *self as f64
166     }
167 }
168
169 5.area();
170 ```
171
172 It is considered poor style to implement methods on such primitive types, even
173 though it is possible.
174
175 This may seem like the Wild West, but there are two other restrictions around
176 implementing traits that prevent this from getting out of hand. The first is
177 that if the trait isn’t defined in your scope, it doesn’t apply. Here’s an
178 example: the standard library provides a [`Write`][write] trait which adds
179 extra functionality to `File`s, for doing file I/O. By default, a `File`
180 won’t have its methods:
181
182 [write]: ../std/io/trait.Write.html
183
184 ```rust,ignore
185 let mut f = std::fs::File::open("foo.txt").ok().expect("Couldn’t open foo.txt");
186 let buf = b"whatever"; // byte string literal. buf: &[u8; 8]
187 let result = f.write(buf);
188 # result.unwrap(); // ignore the error
189 ```
190
191 Here’s the error:
192
193 ```text
194 error: type `std::fs::File` does not implement any method in scope named `write`
195 let result = f.write(buf);
196                ^~~~~~~~~~
197 ```
198
199 We need to `use` the `Write` trait first:
200
201 ```rust,ignore
202 use std::io::Write;
203
204 let mut f = std::fs::File::open("foo.txt").ok().expect("Couldn’t open foo.txt");
205 let buf = b"whatever";
206 let result = f.write(buf);
207 # result.unwrap(); // ignore the error
208 ```
209
210 This will compile without error.
211
212 This means that even if someone does something bad like add methods to `i32`,
213 it won’t affect you, unless you `use` that trait.
214
215 There’s one more restriction on implementing traits. Either the trait or the
216 type you’re writing the `impl` for must be defined by you. So, we could
217 implement the `HasArea` type for `i32`, because `HasArea` is in our code. But
218 if we tried to implement `Float`, a trait provided by Rust, for `i32`, we could
219 not, because neither the trait nor the type are in our code.
220
221 One last thing about traits: generic functions with a trait bound use
222 ‘monomorphization’ (mono: one, morph: form), so they are statically dispatched.
223 What’s that mean? Check out the chapter on [trait objects][to] for more details.
224
225 [to]: trait-objects.html
226
227 # Multiple trait bounds
228
229 You’ve seen that you can bound a generic type parameter with a trait:
230
231 ```rust
232 fn foo<T: Clone>(x: T) {
233     x.clone();
234 }
235 ```
236
237 If you need more than one bound, you can use `+`:
238
239 ```rust
240 use std::fmt::Debug;
241
242 fn foo<T: Clone + Debug>(x: T) {
243     x.clone();
244     println!("{:?}", x);
245 }
246 ```
247
248 `T` now needs to be both `Clone` as well as `Debug`.
249
250 # Where clause
251
252 Writing functions with only a few generic types and a small number of trait
253 bounds isn’t too bad, but as the number increases, the syntax gets increasingly
254 awkward:
255
256 ```rust
257 use std::fmt::Debug;
258
259 fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
260     x.clone();
261     y.clone();
262     println!("{:?}", y);
263 }
264 ```
265
266 The name of the function is on the far left, and the parameter list is on the
267 far right. The bounds are getting in the way.
268
269 Rust has a solution, and it’s called a ‘`where` clause’:
270
271 ```rust
272 use std::fmt::Debug;
273
274 fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
275     x.clone();
276     y.clone();
277     println!("{:?}", y);
278 }
279
280 fn bar<T, K>(x: T, y: K) where T: Clone, K: Clone + Debug {
281     x.clone();
282     y.clone();
283     println!("{:?}", y);
284 }
285
286 fn main() {
287     foo("Hello", "world");
288     bar("Hello", "workd");
289 }
290 ```
291
292 `foo()` uses the syntax we showed earlier, and `bar()` uses a `where` clause.
293 All you need to do is leave off the bounds when defining your type parameters,
294 and then add `where` after the parameter list. For longer lists, whitespace can
295 be added:
296
297 ```rust
298 use std::fmt::Debug;
299
300 fn bar<T, K>(x: T, y: K)
301     where T: Clone,
302           K: Clone + Debug {
303
304     x.clone();
305     y.clone();
306     println!("{:?}", y);
307 }
308 ```
309
310 This flexibility can add clarity in complex situations.
311
312 `where` is also more powerful than the simpler syntax. For example:
313
314 ```rust
315 trait ConvertTo<Output> {
316     fn convert(&self) -> Output;
317 }
318
319 impl ConvertTo<i64> for i32 {
320     fn convert(&self) -> i64 { *self as i64 }
321 }
322
323 // can be called with T == i32
324 fn normal<T: ConvertTo<i64>>(x: &T) -> i64 {
325     x.convert()
326 }
327
328 // can be called with T == i64
329 fn inverse<T>() -> T
330         // this is using ConvertTo as if it were "ConvertFrom<i32>"
331         where i32: ConvertTo<T> {
332     1i32.convert()
333 }
334 ```
335
336 This shows off the additional feature of `where` clauses: they allow bounds
337 where the left-hand side is an arbitrary type (`i32` in this case), not just a
338 plain type parameter (like `T`).
339
340 ## Default methods
341
342 There’s one last feature of traits we should cover: default methods. It’s
343 easiest just to show an example:
344
345 ```rust
346 trait Foo {
347     fn bar(&self);
348
349     fn baz(&self) { println!("We called baz."); }
350 }
351 ```
352
353 Implementors of the `Foo` trait need to implement `bar()`, but they don’t
354 need to implement `baz()`. They’ll get this default behavior. They can
355 override the default if they so choose:
356
357 ```rust
358 # trait Foo {
359 # fn bar(&self);
360 # fn baz(&self) { println!("We called baz."); }
361 # }
362 struct UseDefault;
363
364 impl Foo for UseDefault {
365     fn bar(&self) { println!("We called bar."); }
366 }
367
368 struct OverrideDefault;
369
370 impl Foo for OverrideDefault {
371     fn bar(&self) { println!("We called bar."); }
372
373     fn baz(&self) { println!("Override baz!"); }
374 }
375
376 let default = UseDefault;
377 default.baz(); // prints "We called baz."
378
379 let over = OverrideDefault;
380 over.baz(); // prints "Override baz!"
381 ```
382
383 # Inheritance
384
385 Sometimes, implementing a trait requires implementing another trait:
386
387 ```rust
388 trait Foo {
389     fn foo(&self);
390 }
391
392 trait FooBar : Foo {
393     fn foobar(&self);
394 }
395 ```
396
397 Implementors of `FooBar` must also implement `Foo`, like this:
398
399 ```rust
400 # trait Foo {
401 #     fn foo(&self);
402 # }
403 # trait FooBar : Foo {
404 #     fn foobar(&self);
405 # }
406 struct Baz;
407
408 impl Foo for Baz {
409     fn foo(&self) { println!("foo"); }
410 }
411
412 impl FooBar for Baz {
413     fn foobar(&self) { println!("foobar"); }
414 }
415 ```
416
417 If we forget to implement `Foo`, Rust will tell us:
418
419 ```text
420 error: the trait `main::Foo` is not implemented for the type `main::Baz` [E0277]
421 ```