3 Do you remember the `impl` keyword, used to call a function with [method
14 fn area(&self) -> f64 {
15 std::f64::consts::PI * (self.radius * self.radius)
20 [methodsyntax]: method-syntax.html
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:
33 fn area(&self) -> f64;
36 impl HasArea for Circle {
37 fn area(&self) -> f64 {
38 std::f64::consts::PI * (self.radius * self.radius)
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`.
47 We can use traits to constrain our generics. Consider this function, which
48 does not compile, and gives us a similar error:
51 fn print_area<T>(shape: T) {
52 println!("This shape has an area of {}", shape.area());
59 error: type `T` does not implement any method in scope named `area`
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
68 # fn area(&self) -> f64;
70 fn print_area<T: HasArea>(shape: T) {
71 println!("This shape has an area of {}", shape.area());
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.
79 Here’s an extended example of how this works:
83 fn area(&self) -> f64;
92 impl HasArea for Circle {
93 fn area(&self) -> f64 {
94 std::f64::consts::PI * (self.radius * self.radius)
104 impl HasArea for Square {
105 fn area(&self) -> f64 {
106 self.side * self.side
110 fn print_area<T: HasArea>(shape: T) {
111 println!("This shape has an area of {}", shape.area());
132 This program outputs:
135 This shape has an area of 3.141593
136 This shape has an area of 1
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:
146 We get a compile-time error:
149 error: failed to find an implementation of trait main::HasArea for int
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`
158 fn area(&self) -> f64;
161 impl HasArea for i32 {
162 fn area(&self) -> f64 {
163 println!("this is silly");
172 It is considered poor style to implement methods on such primitive types, even
173 though it is possible.
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:
182 [write]: ../std/io/trait.Write.html
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 error
193 error: type `std::fs::File` does not implement any method in scope named `write`
195 let result = f.write(b"whatever");
199 We need to `use` the `Write` trait first:
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 error
209 This will compile without error.
211 This means that even if someone does something bad like add methods to `i32`,
212 it won’t affect you, unless you `use` that trait.
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
217 if we tried to implement `Float`, a trait provided by Rust, for `i32`, we could
218 not, because neither the trait nor the type are in our code.
220 One last thing about traits: generic functions with a trait bound use
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.
224 [to]: trait-objects.html
226 # Multiple trait bounds
228 You’ve seen that you can bound a generic type parameter with a trait:
231 fn foo<T: Clone>(x: T) {
236 If you need more than one bound, you can use `+`:
241 fn foo<T: Clone + Debug>(x: T) {
247 `T` now needs to be both `Clone` as well as `Debug`.
251 Writing functions with only a few generic types and a small number of trait
252 bounds isn’t too bad, but as the number increases, the syntax gets increasingly
258 fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
265 The name of the function is on the far left, and the parameter list is on the
266 far right. The bounds are getting in the way.
268 Rust has a solution, and it’s called a ‘`where` clause’:
273 fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
279 fn bar<T, K>(x: T, y: K) where T: Clone, K: Clone + Debug {
286 foo("Hello", "world");
287 bar("Hello", "workd");
291 `foo()` uses the syntax we showed earlier, and `bar()` uses a `where` clause.
292 All you need to do is leave off the bounds when defining your type parameters,
293 and then add `where` after the parameter list. For longer lists, whitespace can
299 fn bar<T, K>(x: T, y: K)
309 This flexibility can add clarity in complex situations.
311 `where` is also more powerful than the simpler syntax. For example:
314 trait ConvertTo<Output> {
315 fn convert(&self) -> Output;
318 impl ConvertTo<i64> for i32 {
319 fn convert(&self) -> i64 { *self as i64 }
322 // can be called with T == i32
323 fn normal<T: ConvertTo<i64>>(x: &T) -> i64 {
327 // can be called with T == i64
329 // this is using ConvertTo as if it were "ConvertFrom<i32>"
330 where i32: ConvertTo<T> {
335 This shows off the additional feature of `where` clauses: they allow bounds
336 where the left-hand side is an arbitrary type (`i32` in this case), not just a
337 plain type parameter (like `T`).
341 There’s one last feature of traits we should cover: default methods. It’s
342 easiest just to show an example:
348 fn baz(&self) { println!("We called baz."); }
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
354 override the default if they so choose:
359 # fn baz(&self) { println!("We called baz."); }
363 impl Foo for UseDefault {
364 fn bar(&self) { println!("We called bar."); }
367 struct OverrideDefault;
369 impl Foo for OverrideDefault {
370 fn bar(&self) { println!("We called bar."); }
372 fn baz(&self) { println!("Override baz!"); }
375 let default = UseDefault;
376 default.baz(); // prints "We called baz."
378 let over = OverrideDefault;
379 over.baz(); // prints "Override baz!"
384 Sometimes, implementing a trait requires implementing another trait:
396 Implementors of `FooBar` must also implement `Foo`, like this:
402 # trait FooBar : Foo {
408 fn foo(&self) { println!("foo"); }
411 impl FooBar for Baz {
412 fn foobar(&self) { println!("foobar"); }
416 If we forget to implement `Foo`, Rust will tell us:
419 error: the trait `main::Foo` is not implemented for the type `main::Baz` [E0277]