1
1
% Traits
2
2
3
- Do you remember the ` impl ` keyword, used to call a function with method
4
- syntax?
3
+ Do you remember the ` impl ` keyword, used to call a function with [ method
4
+ syntax] [ methodsyntax ] ?
5
5
6
- ``` {rust}
7
- # #![feature(core)]
6
+ ``` rust
8
7
struct Circle {
9
8
x : f64 ,
10
9
y : f64 ,
@@ -18,11 +17,12 @@ impl Circle {
18
17
}
19
18
```
20
19
20
+ [ methodsyntax ] : method-syntax.html
21
+
21
22
Traits are similar, except that we define a trait with just the method
22
23
signature, then implement the trait for that struct. Like this:
23
24
24
- ``` {rust}
25
- # #![feature(core)]
25
+ ``` rust
26
26
struct Circle {
27
27
x : f64 ,
28
28
y : f64 ,
@@ -41,20 +41,13 @@ impl HasArea for Circle {
41
41
```
42
42
43
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,
44
+ but we don’ t define a body, just a type signature. When we ` impl ` a trait,
45
45
we use ` impl Trait for Item ` , rather than just ` impl Item ` .
46
46
47
- So what's the big deal? Remember the error we were getting with our generic
48
- ` inverse ` function?
49
-
50
- ``` text
51
- error: binary operation `==` cannot be applied to type `T`
52
- ```
53
-
54
47
We can use traits to constrain our generics. Consider this function, which
55
48
does not compile, and gives us a similar error:
56
49
57
- ``` { rust,ignore}
50
+ ``` rust,ignore
58
51
fn print_area<T>(shape: T) {
59
52
println!("This shape has an area of {}", shape.area());
60
53
}
@@ -66,11 +59,11 @@ Rust complains:
66
59
error: type `T` does not implement any method in scope named `area`
67
60
```
68
61
69
- Because ` T ` can be any type, we can' t be sure that it implements the ` area `
70
- method. But we can add a * trait constraint* to our generic ` T ` , ensuring
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
71
64
that it does:
72
65
73
- ``` { rust}
66
+ ``` rust
74
67
# trait HasArea {
75
68
# fn area (& self ) -> f64 ;
76
69
# }
@@ -83,10 +76,9 @@ The syntax `<T: HasArea>` means `any type that implements the HasArea trait`.
83
76
Because traits define function type signatures, we can be sure that any type
84
77
which implements ` HasArea ` will have an ` .area() ` method.
85
78
86
- Here' s an extended example of how this works:
79
+ Here’ s an extended example of how this works:
87
80
88
- ``` {rust}
89
- # #![feature(core)]
81
+ ``` rust
90
82
trait HasArea {
91
83
fn area (& self ) -> f64 ;
92
84
}
@@ -144,10 +136,10 @@ This shape has an area of 3.141593
144
136
This shape has an area of 1
145
137
```
146
138
147
- As you can see, ` print_area ` is now generic, but also ensures that we
148
- have passed in the correct types. If we pass in an incorrect type:
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:
149
141
150
- ``` { rust,ignore}
142
+ ``` rust,ignore
151
143
print_area(5);
152
144
```
153
145
@@ -157,11 +149,11 @@ We get a compile-time error:
157
149
error: failed to find an implementation of trait main::HasArea for int
158
150
```
159
151
160
- So far, we' ve only added trait implementations to structs, but you can
161
- implement a trait for any type. So technically, we _ could_ implement
162
- ` HasArea ` for ` i32 ` :
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 ` :
163
155
164
- ``` { rust}
156
+ ``` rust
165
157
trait HasArea {
166
158
fn area (& self ) -> f64 ;
167
159
}
@@ -181,100 +173,55 @@ It is considered poor style to implement methods on such primitive types, even
181
173
though it is possible.
182
174
183
175
This may seem like the Wild West, but there are two other restrictions around
184
- implementing traits that prevent this from getting out of hand. First, traits
185
- must be ` use ` d in any scope where you wish to use the trait's method. So for
186
- example, this does not work:
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:
187
181
188
- ``` {rust,ignore}
189
- mod shapes {
190
- use std::f64::consts;
182
+ [ write ] : ../std/io/trait.Write.html
191
183
192
- trait HasArea {
193
- fn area(&self) -> f64;
194
- }
195
-
196
- struct Circle {
197
- x: f64,
198
- y: f64,
199
- radius: f64,
200
- }
201
-
202
- impl HasArea for Circle {
203
- fn area(&self) -> f64 {
204
- consts::PI * (self.radius * self.radius)
205
- }
206
- }
207
- }
208
-
209
- fn main() {
210
- let c = shapes::Circle {
211
- x: 0.0f64,
212
- y: 0.0f64,
213
- radius: 1.0f64,
214
- };
215
-
216
- println!("{}", c.area());
217
- }
184
+ ``` rust,ignore
185
+ let mut f = std::fs::File::open("foo.txt").ok().expect("Couldn’t open foo.txt");
186
+ let result = f.write("whatever".as_bytes());
187
+ # result.unwrap(); // ignore the erorr
218
188
```
219
189
220
- Now that we've moved the structs and traits into their own module, we get an
221
- error:
190
+ Here’s the error:
222
191
223
192
``` text
224
- error: type `shapes::Circle` does not implement any method in scope named `area`
225
- ```
226
-
227
- If we add a ` use ` line right above ` main ` and make the right things public,
228
- everything is fine:
229
-
230
- ``` {rust}
231
- # #![feature(core)]
232
- mod shapes {
233
- use std::f64::consts;
234
-
235
- pub trait HasArea {
236
- fn area(&self) -> f64;
237
- }
238
-
239
- pub struct Circle {
240
- pub x: f64,
241
- pub y: f64,
242
- pub radius: f64,
243
- }
193
+ error: type `std::fs::File` does not implement any method in scope named `write`
244
194
245
- impl HasArea for Circle {
246
- fn area(&self) -> f64 {
247
- consts::PI * (self.radius * self.radius)
248
- }
249
- }
250
- }
195
+ let result = f.write(b”whatever”);
196
+ ^~~~~~~~~~~~~~~~~~
197
+ ```
251
198
252
- use shapes::HasArea;
199
+ We need to ` use ` the ` Write ` trait first:
253
200
254
- fn main() {
255
- let c = shapes::Circle {
256
- x: 0.0f64,
257
- y: 0.0f64,
258
- radius: 1.0f64,
259
- };
201
+ ``` rust,ignore
202
+ use std::io::Write;
260
203
261
- println!("{}", c.area());
262
- }
204
+ let mut f = std::fs::File::open("foo.txt").ok().expect("Couldn’t open foo.txt");
205
+ let result = f.write("whatever".as_bytes());
206
+ # result.unwrap(); // ignore the erorr
263
207
```
264
208
209
+ This will compile without error.
210
+
265
211
This means that even if someone does something bad like add methods to ` int ` ,
266
- it won' t affect you, unless you ` use ` that trait.
212
+ it won’ t affect you, unless you ` use ` that trait.
267
213
268
- There' s one more restriction on implementing traits. Either the trait or the
269
- type you' re writing the ` impl ` for must be inside your crate . So, we could
270
- implement the ` HasArea ` type for ` i32 ` , because ` HasArea ` is in our crate. But
214
+ There’ s one more restriction on implementing traits. Either the trait or the
215
+ type you’ re writing the ` impl ` for must be defined by you . So, we could
216
+ implement the ` HasArea ` type for ` i32 ` , because ` HasArea ` is in our code. But
271
217
if we tried to implement ` Float ` , a trait provided by Rust, for ` i32 ` , we could
272
- not, because both the trait and the type aren't in our crate .
218
+ not, because neither the trait nor the type are in our code .
273
219
274
220
One last thing about traits: generic functions with a trait bound use
275
- * monomorphization* (* mono* : one, * morph* : form), so they are statically
276
- dispatched. What's that mean? Check out the chapter on [ trait
277
- objects] ( trait-objects.html ) for more.
221
+ ‘monomorphization’ (mono: one, morph: form), so they are statically dispatched.
222
+ What’s that mean? Check out the chapter on [ trait objects] [ to ] for more details.
223
+
224
+ [ to ] : trait-objects.html
278
225
279
226
## Multiple trait bounds
280
227
@@ -302,7 +249,7 @@ fn foo<T: Clone + Debug>(x: T) {
302
249
## Where clause
303
250
304
251
Writing functions with only a few generic types and a small number of trait
305
- bounds isn' t too bad, but as the number increases, the syntax gets increasingly
252
+ bounds isn’ t too bad, but as the number increases, the syntax gets increasingly
306
253
awkward:
307
254
308
255
```
@@ -318,7 +265,7 @@ fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
318
265
The name of the function is on the far left, and the parameter list is on the
319
266
far right. The bounds are getting in the way.
320
267
321
- Rust has a solution, and it' s called a ' ` where ` clause' :
268
+ Rust has a solution, and it’ s called a ‘ ` where ` clause’ :
322
269
323
270
```
324
271
use std::fmt::Debug;
@@ -389,84 +336,9 @@ This shows off the additional feature of `where` clauses: they allow bounds
389
336
where the left-hand side is an arbitrary type (` i32 ` in this case), not just a
390
337
plain type parameter (like ` T ` ).
391
338
392
- ## Our ` inverse ` Example
393
-
394
- Back in [ Generics] ( generics.html ) , we were trying to write code like this:
395
-
396
- ``` {rust,ignore}
397
- fn inverse<T>(x: T) -> Result<T, String> {
398
- if x == 0.0 { return Err("x cannot be zero!".to_string()); }
399
-
400
- Ok(1.0 / x)
401
- }
402
- ```
403
-
404
- If we try to compile it, we get this error:
405
-
406
- ``` text
407
- error: binary operation `==` cannot be applied to type `T`
408
- ```
409
-
410
- This is because ` T ` is too generic: we don't know if a random ` T ` can be
411
- compared. For that, we can use trait bounds. It doesn't quite work, but try
412
- this:
413
-
414
- ``` {rust,ignore}
415
- fn inverse<T: PartialEq>(x: T) -> Result<T, String> {
416
- if x == 0.0 { return Err("x cannot be zero!".to_string()); }
417
-
418
- Ok(1.0 / x)
419
- }
420
- ```
421
-
422
- You should get this error:
423
-
424
- ``` text
425
- error: mismatched types:
426
- expected `T`,
427
- found `_`
428
- (expected type parameter,
429
- found floating-point variable)
430
- ```
431
-
432
- So this won't work. While our ` T ` is ` PartialEq ` , we expected to have another ` T ` ,
433
- but instead, we found a floating-point variable. We need a different bound. ` Float `
434
- to the rescue:
435
-
436
- ```
437
- # #![feature(std_misc)]
438
- use std::num::Float;
439
-
440
- fn inverse<T: Float>(x: T) -> Result<T, String> {
441
- if x == Float::zero() { return Err("x cannot be zero!".to_string()) }
442
-
443
- let one: T = Float::one();
444
- Ok(one / x)
445
- }
446
- ```
447
-
448
- We've had to replace our generic ` 0.0 ` and ` 1.0 ` with the appropriate methods
449
- from the ` Float ` trait. Both ` f32 ` and ` f64 ` implement ` Float ` , so our function
450
- works just fine:
451
-
452
- ```
453
- # #![feature(std_misc)]
454
- # use std::num::Float;
455
- # fn inverse<T: Float>(x: T) -> Result<T, String> {
456
- # if x == Float::zero() { return Err("x cannot be zero!".to_string()) }
457
- # let one: T = Float::one();
458
- # Ok(one / x)
459
- # }
460
- println!("the inverse of {} is {:?}", 2.0f32, inverse(2.0f32));
461
- println!("the inverse of {} is {:?}", 2.0f64, inverse(2.0f64));
462
-
463
- println!("the inverse of {} is {:?}", 0.0f32, inverse(0.0f32));
464
- println!("the inverse of {} is {:?}", 0.0f64, inverse(0.0f64));
465
- ```
466
-
467
339
## Default methods
468
340
469
- There' s one last feature of traits we should cover: default methods. It' s
341
+ There’ s one last feature of traits we should cover: default methods. It’ s
470
342
easiest just to show an example:
471
343
472
344
``` rust
@@ -477,8 +349,8 @@ trait Foo {
477
349
}
478
350
```
479
351
480
- Implementors of the ` Foo ` trait need to implement ` bar() ` , but they don' t
481
- need to implement ` baz() ` . They' ll get this default behavior. They can
352
+ Implementors of the ` Foo ` trait need to implement ` bar() ` , but they don’ t
353
+ need to implement ` baz() ` . They’ ll get this default behavior. They can
482
354
override the default if they so choose:
483
355
484
356
``` rust
0 commit comments