11% Traits
22
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 ] ?
55
6- ``` {rust}
7- # #![feature(core)]
6+ ``` rust
87struct Circle {
98 x : f64 ,
109 y : f64 ,
@@ -18,11 +17,12 @@ impl Circle {
1817}
1918```
2019
20+ [ methodsyntax ] : method-syntax.html
21+
2122Traits are similar, except that we define a trait with just the method
2223signature, then implement the trait for that struct. Like this:
2324
24- ``` {rust}
25- # #![feature(core)]
25+ ``` rust
2626struct Circle {
2727 x : f64 ,
2828 y : f64 ,
@@ -41,20 +41,13 @@ impl HasArea for Circle {
4141```
4242
4343As 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,
4545we use ` impl Trait for Item ` , rather than just ` impl Item ` .
4646
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-
5447We can use traits to constrain our generics. Consider this function, which
5548does not compile, and gives us a similar error:
5649
57- ``` { rust,ignore}
50+ ``` rust,ignore
5851fn print_area<T>(shape: T) {
5952 println!("This shape has an area of {}", shape.area());
6053}
@@ -66,11 +59,11 @@ Rust complains:
6659error: type `T` does not implement any method in scope named `area`
6760```
6861
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
7164that it does:
7265
73- ``` { rust}
66+ ``` rust
7467# trait HasArea {
7568# fn area (& self ) -> f64 ;
7669# }
@@ -83,10 +76,9 @@ The syntax `<T: HasArea>` means `any type that implements the HasArea trait`.
8376Because traits define function type signatures, we can be sure that any type
8477which implements ` HasArea ` will have an ` .area() ` method.
8578
86- Here' s an extended example of how this works:
79+ Here’ s an extended example of how this works:
8780
88- ``` {rust}
89- # #![feature(core)]
81+ ``` rust
9082trait HasArea {
9183 fn area (& self ) -> f64 ;
9284}
@@ -144,10 +136,10 @@ This shape has an area of 3.141593
144136This shape has an area of 1
145137```
146138
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:
149141
150- ``` { rust,ignore}
142+ ``` rust,ignore
151143print_area(5);
152144```
153145
@@ -157,11 +149,11 @@ We get a compile-time error:
157149error: failed to find an implementation of trait main::HasArea for int
158150```
159151
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 ` :
163155
164- ``` { rust}
156+ ``` rust
165157trait HasArea {
166158 fn area (& self ) -> f64 ;
167159}
@@ -181,100 +173,55 @@ It is considered poor style to implement methods on such primitive types, even
181173though it is possible.
182174
183175This 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:
187181
188- ``` {rust,ignore}
189- mod shapes {
190- use std::f64::consts;
182+ [ write ] : ../std/io/trait.Write.html
191183
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
218188```
219189
220- Now that we've moved the structs and traits into their own module, we get an
221- error:
190+ Here’s the error:
222191
223192``` 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`
244194
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+ ```
251198
252- use shapes::HasArea;
199+ We need to ` use ` the ` Write ` trait first:
253200
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;
260203
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
263207```
264208
209+ This will compile without error.
210+
265211This 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.
267213
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
271217if 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 .
273219
274220One 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
278225
279226## Multiple trait bounds
280227
@@ -302,7 +249,7 @@ fn foo<T: Clone + Debug>(x: T) {
302249## Where clause
303250
304251Writing 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
306253awkward:
307254
308255```
@@ -318,7 +265,7 @@ fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
318265The name of the function is on the far left, and the parameter list is on the
319266far right. The bounds are getting in the way.
320267
321- Rust has a solution, and it' s called a ' ` where ` clause' :
268+ Rust has a solution, and it’ s called a ‘ ` where ` clause’ :
322269
323270```
324271use std::fmt::Debug;
@@ -389,84 +336,9 @@ This shows off the additional feature of `where` clauses: they allow bounds
389336where the left-hand side is an arbitrary type (` i32 ` in this case), not just a
390337plain type parameter (like ` T ` ).
391338
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-
467339## Default methods
468340
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
470342easiest just to show an example:
471343
472344``` rust
@@ -477,8 +349,8 @@ trait Foo {
477349}
478350```
479351
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
482354override the default if they so choose:
483355
484356``` rust
0 commit comments