]> git.lizzy.rs Git - rust.git/blob - src/doc/trpl/traits.md
Resolve unused_parens compilation warning
[rust.git] / src / doc / trpl / traits.md
1 % Traits
2
3 A trait is a language feature that tells the Rust compiler about
4 functionality a type must provide.
5
6 Do you remember the `impl` keyword, used to call a function with [method
7 syntax][methodsyntax]?
8
9 ```rust
10 struct Circle {
11     x: f64,
12     y: f64,
13     radius: f64,
14 }
15
16 impl Circle {
17     fn area(&self) -> f64 {
18         std::f64::consts::PI * (self.radius * self.radius)
19     }
20 }
21 ```
22
23 [methodsyntax]: method-syntax.html
24
25 Traits are similar, except that we define a trait with just the method
26 signature, then implement the trait for that struct. Like this:
27
28 ```rust
29 struct Circle {
30     x: f64,
31     y: f64,
32     radius: f64,
33 }
34
35 trait HasArea {
36     fn area(&self) -> f64;
37 }
38
39 impl HasArea for Circle {
40     fn area(&self) -> f64 {
41         std::f64::consts::PI * (self.radius * self.radius)
42     }
43 }
44 ```
45
46 As you can see, the `trait` block looks very similar to the `impl` block,
47 but we don’t define a body, just a type signature. When we `impl` a trait,
48 we use `impl Trait for Item`, rather than just `impl Item`.
49
50 ## Traits bounds for generic functions
51
52 Traits are useful because they allow a type to make certain promises about its
53 behavior. Generic functions can exploit this to constrain the types they
54 accept. Consider this function, which does not compile:
55
56 ```rust,ignore
57 fn print_area<T>(shape: T) {
58     println!("This shape has an area of {}", shape.area());
59 }
60 ```
61
62 Rust complains:
63
64 ```text
65 error: no method named `area` found for type `T` in the current scope
66 ```
67
68 Because `T` can be any type, we can’t be sure that it implements the `area`
69 method. But we can add a ‘trait constraint’ to our generic `T`, ensuring
70 that it does:
71
72 ```rust
73 # trait HasArea {
74 #     fn area(&self) -> f64;
75 # }
76 fn print_area<T: HasArea>(shape: T) {
77     println!("This shape has an area of {}", shape.area());
78 }
79 ```
80
81 The syntax `<T: HasArea>` means “any type that implements the `HasArea` trait.”
82 Because traits define function type signatures, we can be sure that any type
83 which implements `HasArea` will have an `.area()` method.
84
85 Here’s an extended example of how this works:
86
87 ```rust
88 trait HasArea {
89     fn area(&self) -> f64;
90 }
91
92 struct Circle {
93     x: f64,
94     y: f64,
95     radius: f64,
96 }
97
98 impl HasArea for Circle {
99     fn area(&self) -> f64 {
100         std::f64::consts::PI * (self.radius * self.radius)
101     }
102 }
103
104 struct Square {
105     x: f64,
106     y: f64,
107     side: f64,
108 }
109
110 impl HasArea for Square {
111     fn area(&self) -> f64 {
112         self.side * self.side
113     }
114 }
115
116 fn print_area<T: HasArea>(shape: T) {
117     println!("This shape has an area of {}", shape.area());
118 }
119
120 fn main() {
121     let c = Circle {
122         x: 0.0f64,
123         y: 0.0f64,
124         radius: 1.0f64,
125     };
126
127     let s = Square {
128         x: 0.0f64,
129         y: 0.0f64,
130         side: 1.0f64,
131     };
132
133     print_area(c);
134     print_area(s);
135 }
136 ```
137
138 This program outputs:
139
140 ```text
141 This shape has an area of 3.141593
142 This shape has an area of 1
143 ```
144
145 As you can see, `print_area` is now generic, but also ensures that we have
146 passed in the correct types. If we pass in an incorrect type:
147
148 ```rust,ignore
149 print_area(5);
150 ```
151
152 We get a compile-time error:
153
154 ```text
155 error: the trait `HasArea` is not implemented for the type `_` [E0277]
156 ```
157
158 ## Traits bounds for generic structs
159
160 Your generic structs can also benefit from trait constraints. All you need to
161 do is append the constraint when you declare type parameters. Here is a new
162 type `Rectangle<T>` and its operation `is_square()`:
163
164 ```rust
165 struct Rectangle<T> {
166     x: T,
167     y: T,
168     width: T,
169     height: T,
170 }
171
172 impl<T: PartialEq> Rectangle<T> {
173     fn is_square(&self) -> bool {
174         self.width == self.height
175     }
176 }
177
178 fn main() {
179     let mut r = Rectangle {
180         x: 0,
181         y: 0,
182         width: 47,
183         height: 47,
184     };
185
186     assert!(r.is_square());
187
188     r.height = 42;
189     assert!(!r.is_square());
190 }
191 ```
192
193 `is_square()` needs to check that the sides are equal, so the sides must be of
194 a type that implements the [`core::cmp::PartialEq`][PartialEq] trait:
195
196 ```ignore
197 impl<T: PartialEq> Rectangle<T> { ... }
198 ```
199
200 Now, a rectangle can be defined in terms of any type that can be compared for
201 equality.
202
203 [PartialEq]: ../core/cmp/trait.PartialEq.html
204
205 Here we defined a new struct `Rectangle` that accepts numbers of any
206 precision—really, objects of pretty much any type—as long as they can be
207 compared for equality. Could we do the same for our `HasArea` structs, `Square`
208 and `Circle`? Yes, but they need multiplication, and to work with that we need
209 to know more about [operator traits][operators-and-overloading].
210
211 [operators-and-overloading]: operators-and-overloading.html
212
213 # Rules for implementing traits
214
215 So far, we’ve only added trait implementations to structs, but you can
216 implement a trait for any type. So technically, we _could_ implement `HasArea`
217 for `i32`:
218
219 ```rust
220 trait HasArea {
221     fn area(&self) -> f64;
222 }
223
224 impl HasArea for i32 {
225     fn area(&self) -> f64 {
226         println!("this is silly");
227
228         *self as f64
229     }
230 }
231
232 5.area();
233 ```
234
235 It is considered poor style to implement methods on such primitive types, even
236 though it is possible.
237
238 This may seem like the Wild West, but there are two restrictions around
239 implementing traits that prevent this from getting out of hand. The first is
240 that if the trait isn’t defined in your scope, it doesn’t apply. Here’s an
241 example: the standard library provides a [`Write`][write] trait which adds
242 extra functionality to `File`s, for doing file I/O. By default, a `File`
243 won’t have its methods:
244
245 [write]: ../std/io/trait.Write.html
246
247 ```rust,ignore
248 let mut f = std::fs::File::open("foo.txt").ok().expect("Couldn’t open foo.txt");
249 let buf = b"whatever"; // byte string literal. buf: &[u8; 8]
250 let result = f.write(buf);
251 # result.unwrap(); // ignore the error
252 ```
253
254 Here’s the error:
255
256 ```text
257 error: type `std::fs::File` does not implement any method in scope named `write`
258 let result = f.write(buf);
259                ^~~~~~~~~~
260 ```
261
262 We need to `use` the `Write` trait first:
263
264 ```rust,ignore
265 use std::io::Write;
266
267 let mut f = std::fs::File::open("foo.txt").ok().expect("Couldn’t open foo.txt");
268 let buf = b"whatever";
269 let result = f.write(buf);
270 # result.unwrap(); // ignore the error
271 ```
272
273 This will compile without error.
274
275 This means that even if someone does something bad like add methods to `i32`,
276 it won’t affect you, unless you `use` that trait.
277
278 There’s one more restriction on implementing traits: either the trait, or the
279 type you’re writing the `impl` for, must be defined by you. So, we could
280 implement the `HasArea` type for `i32`, because `HasArea` is in our code. But
281 if we tried to implement `ToString`, a trait provided by Rust, for `i32`, we could
282 not, because neither the trait nor the type are in our code.
283
284 One last thing about traits: generic functions with a trait bound use
285 ‘monomorphization’ (mono: one, morph: form), so they are statically dispatched.
286 What’s that mean? Check out the chapter on [trait objects][to] for more details.
287
288 [to]: trait-objects.html
289
290 # Multiple trait bounds
291
292 You’ve seen that you can bound a generic type parameter with a trait:
293
294 ```rust
295 fn foo<T: Clone>(x: T) {
296     x.clone();
297 }
298 ```
299
300 If you need more than one bound, you can use `+`:
301
302 ```rust
303 use std::fmt::Debug;
304
305 fn foo<T: Clone + Debug>(x: T) {
306     x.clone();
307     println!("{:?}", x);
308 }
309 ```
310
311 `T` now needs to be both `Clone` as well as `Debug`.
312
313 # Where clause
314
315 Writing functions with only a few generic types and a small number of trait
316 bounds isn’t too bad, but as the number increases, the syntax gets increasingly
317 awkward:
318
319 ```rust
320 use std::fmt::Debug;
321
322 fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
323     x.clone();
324     y.clone();
325     println!("{:?}", y);
326 }
327 ```
328
329 The name of the function is on the far left, and the parameter list is on the
330 far right. The bounds are getting in the way.
331
332 Rust has a solution, and it’s called a ‘`where` clause’:
333
334 ```rust
335 use std::fmt::Debug;
336
337 fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
338     x.clone();
339     y.clone();
340     println!("{:?}", y);
341 }
342
343 fn bar<T, K>(x: T, y: K) where T: Clone, K: Clone + Debug {
344     x.clone();
345     y.clone();
346     println!("{:?}", y);
347 }
348
349 fn main() {
350     foo("Hello", "world");
351     bar("Hello", "world");
352 }
353 ```
354
355 `foo()` uses the syntax we showed earlier, and `bar()` uses a `where` clause.
356 All you need to do is leave off the bounds when defining your type parameters,
357 and then add `where` after the parameter list. For longer lists, whitespace can
358 be added:
359
360 ```rust
361 use std::fmt::Debug;
362
363 fn bar<T, K>(x: T, y: K)
364     where T: Clone,
365           K: Clone + Debug {
366
367     x.clone();
368     y.clone();
369     println!("{:?}", y);
370 }
371 ```
372
373 This flexibility can add clarity in complex situations.
374
375 `where` is also more powerful than the simpler syntax. For example:
376
377 ```rust
378 trait ConvertTo<Output> {
379     fn convert(&self) -> Output;
380 }
381
382 impl ConvertTo<i64> for i32 {
383     fn convert(&self) -> i64 { *self as i64 }
384 }
385
386 // can be called with T == i32
387 fn normal<T: ConvertTo<i64>>(x: &T) -> i64 {
388     x.convert()
389 }
390
391 // can be called with T == i64
392 fn inverse<T>() -> T
393         // this is using ConvertTo as if it were "ConvertTo<i64>"
394         where i32: ConvertTo<T> {
395     42.convert()
396 }
397 ```
398
399 This shows off the additional feature of `where` clauses: they allow bounds
400 where the left-hand side is an arbitrary type (`i32` in this case), not just a
401 plain type parameter (like `T`).
402
403 # Default methods
404
405 If you already know how a typical implementor will define a method, you can
406 let your trait supply a default:
407
408 ```rust
409 trait Foo {
410     fn is_valid(&self) -> bool;
411
412     fn is_invalid(&self) -> bool { !self.is_valid() }
413 }
414 ```
415
416 Implementors of the `Foo` trait need to implement `is_valid()`, but they don’t
417 need to implement `is_invalid()`. They’ll get this default behavior. They can
418 override the default if they so choose:
419
420 ```rust
421 # trait Foo {
422 #     fn is_valid(&self) -> bool;
423 #
424 #     fn is_invalid(&self) -> bool { !self.is_valid() }
425 # }
426 struct UseDefault;
427
428 impl Foo for UseDefault {
429     fn is_valid(&self) -> bool {
430         println!("Called UseDefault.is_valid.");
431         true
432     }
433 }
434
435 struct OverrideDefault;
436
437 impl Foo for OverrideDefault {
438     fn is_valid(&self) -> bool {
439         println!("Called OverrideDefault.is_valid.");
440         true
441     }
442
443     fn is_invalid(&self) -> bool {
444         println!("Called OverrideDefault.is_invalid!");
445         true // this implementation is a self-contradiction!
446     }
447 }
448
449 let default = UseDefault;
450 assert!(!default.is_invalid()); // prints "Called UseDefault.is_valid."
451
452 let over = OverrideDefault;
453 assert!(over.is_invalid()); // prints "Called OverrideDefault.is_invalid!"
454 ```
455
456 # Inheritance
457
458 Sometimes, implementing a trait requires implementing another trait:
459
460 ```rust
461 trait Foo {
462     fn foo(&self);
463 }
464
465 trait FooBar : Foo {
466     fn foobar(&self);
467 }
468 ```
469
470 Implementors of `FooBar` must also implement `Foo`, like this:
471
472 ```rust
473 # trait Foo {
474 #     fn foo(&self);
475 # }
476 # trait FooBar : Foo {
477 #     fn foobar(&self);
478 # }
479 struct Baz;
480
481 impl Foo for Baz {
482     fn foo(&self) { println!("foo"); }
483 }
484
485 impl FooBar for Baz {
486     fn foobar(&self) { println!("foobar"); }
487 }
488 ```
489
490 If we forget to implement `Foo`, Rust will tell us:
491
492 ```text
493 error: the trait `main::Foo` is not implemented for the type `main::Baz` [E0277]
494 ```
495
496 # Deriving
497
498 Implementing traits like `Debug` and `Default` over and over again can become
499 quite tedious. For that reason, Rust provides an [attribute][attributes] that
500 allows you to let Rust automatically implement traits for you:
501
502 ```rust
503 #[derive(Debug)]
504 struct Foo;
505
506 fn main() {
507     println!("{:?}", Foo);
508 }
509 ```
510
511 [attributes]: attributes.html
512
513 However, deriving is limited to a certain set of traits:
514
515 - [`Clone`](../core/clone/trait.Clone.html)
516 - [`Copy`](../core/marker/trait.Copy.html)
517 - [`Debug`](../core/fmt/trait.Debug.html)
518 - [`Default`](../core/default/trait.Default.html)
519 - [`Eq`](../core/cmp/trait.Eq.html)
520 - [`Hash`](../core/hash/trait.Hash.html)
521 - [`Ord`](../core/cmp/trait.Ord.html)
522 - [`PartialEq`](../core/cmp/trait.PartialEq.html)
523 - [`PartialOrd`](../core/cmp/trait.PartialOrd.html)