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 buf = b"whatever"; // byte string literal. buf: &[u8; 8]
187 let result = f.write(buf);
188 # result.unwrap(); // ignore the error
194 error: type `std::fs::File` does not implement any method in scope named `write`
195 let result = f.write(buf);
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 buf = b"whatever";
206 let result = f.write(buf);
207 # result.unwrap(); // ignore the error
210 This will compile without error.
212 This means that even if someone does something bad like add methods to `i32`,
213 it won’t affect you, unless you `use` that trait.
215 There’s one more restriction on implementing traits. Either the trait or the
216 type you’re writing the `impl` for must be defined by you. So, we could
217 implement the `HasArea` type for `i32`, because `HasArea` is in our code. But
218 if we tried to implement `Float`, a trait provided by Rust, for `i32`, we could
219 not, because neither the trait nor the type are in our code.
221 One last thing about traits: generic functions with a trait bound use
222 ‘monomorphization’ (mono: one, morph: form), so they are statically dispatched.
223 What’s that mean? Check out the chapter on [trait objects][to] for more details.
225 [to]: trait-objects.html
227 # Multiple trait bounds
229 You’ve seen that you can bound a generic type parameter with a trait:
232 fn foo<T: Clone>(x: T) {
237 If you need more than one bound, you can use `+`:
242 fn foo<T: Clone + Debug>(x: T) {
248 `T` now needs to be both `Clone` as well as `Debug`.
252 Writing functions with only a few generic types and a small number of trait
253 bounds isn’t too bad, but as the number increases, the syntax gets increasingly
259 fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
266 The name of the function is on the far left, and the parameter list is on the
267 far right. The bounds are getting in the way.
269 Rust has a solution, and it’s called a ‘`where` clause’:
274 fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
280 fn bar<T, K>(x: T, y: K) where T: Clone, K: Clone + Debug {
287 foo("Hello", "world");
288 bar("Hello", "workd");
292 `foo()` uses the syntax we showed earlier, and `bar()` uses a `where` clause.
293 All you need to do is leave off the bounds when defining your type parameters,
294 and then add `where` after the parameter list. For longer lists, whitespace can
300 fn bar<T, K>(x: T, y: K)
310 This flexibility can add clarity in complex situations.
312 `where` is also more powerful than the simpler syntax. For example:
315 trait ConvertTo<Output> {
316 fn convert(&self) -> Output;
319 impl ConvertTo<i64> for i32 {
320 fn convert(&self) -> i64 { *self as i64 }
323 // can be called with T == i32
324 fn normal<T: ConvertTo<i64>>(x: &T) -> i64 {
328 // can be called with T == i64
330 // this is using ConvertTo as if it were "ConvertFrom<i32>"
331 where i32: ConvertTo<T> {
336 This shows off the additional feature of `where` clauses: they allow bounds
337 where the left-hand side is an arbitrary type (`i32` in this case), not just a
338 plain type parameter (like `T`).
342 There’s one last feature of traits we should cover: default methods. It’s
343 easiest just to show an example:
349 fn baz(&self) { println!("We called baz."); }
353 Implementors of the `Foo` trait need to implement `bar()`, but they don’t
354 need to implement `baz()`. They’ll get this default behavior. They can
355 override the default if they so choose:
360 # fn baz(&self) { println!("We called baz."); }
364 impl Foo for UseDefault {
365 fn bar(&self) { println!("We called bar."); }
368 struct OverrideDefault;
370 impl Foo for OverrideDefault {
371 fn bar(&self) { println!("We called bar."); }
373 fn baz(&self) { println!("Override baz!"); }
376 let default = UseDefault;
377 default.baz(); // prints "We called baz."
379 let over = OverrideDefault;
380 over.baz(); // prints "Override baz!"
385 Sometimes, implementing a trait requires implementing another trait:
397 Implementors of `FooBar` must also implement `Foo`, like this:
403 # trait FooBar : Foo {
409 fn foo(&self) { println!("foo"); }
412 impl FooBar for Baz {
413 fn foobar(&self) { println!("foobar"); }
417 If we forget to implement `Foo`, Rust will tell us:
420 error: the trait `main::Foo` is not implemented for the type `main::Baz` [E0277]