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