3 A trait is a language feature that tells the Rust compiler about
4 functionality a type must provide.
6 Do you remember the `impl` keyword, used to call a function with [method
17 fn area(&self) -> f64 {
18 std::f64::consts::PI * (self.radius * self.radius)
23 [methodsyntax]: method-syntax.html
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:
36 fn area(&self) -> f64;
39 impl HasArea for Circle {
40 fn area(&self) -> f64 {
41 std::f64::consts::PI * (self.radius * self.radius)
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`.
50 We can use traits to constrain our generics. Consider this function, which
54 fn print_area<T>(shape: T) {
55 println!("This shape has an area of {}", shape.area());
62 error: no method named `area` found for type `T` in the current scope
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
71 # fn area(&self) -> f64;
73 fn print_area<T: HasArea>(shape: T) {
74 println!("This shape has an area of {}", shape.area());
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.
82 Here’s an extended example of how this works:
86 fn area(&self) -> f64;
95 impl HasArea for Circle {
96 fn area(&self) -> f64 {
97 std::f64::consts::PI * (self.radius * self.radius)
107 impl HasArea for Square {
108 fn area(&self) -> f64 {
109 self.side * self.side
113 fn print_area<T: HasArea>(shape: T) {
114 println!("This shape has an area of {}", shape.area());
135 This program outputs:
138 This shape has an area of 3.141593
139 This shape has an area of 1
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:
149 We get a compile-time error:
152 error: the trait `HasArea` is not implemented for the type `_` [E0277]
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`
161 fn area(&self) -> f64;
164 impl HasArea for i32 {
165 fn area(&self) -> f64 {
166 println!("this is silly");
175 It is considered poor style to implement methods on such primitive types, even
176 though it is possible.
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:
185 [write]: ../std/io/trait.Write.html
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
197 error: type `std::fs::File` does not implement any method in scope named `write`
198 let result = f.write(buf);
202 We need to `use` the `Write` trait first:
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
213 This will compile without error.
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.
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.
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.
228 [to]: trait-objects.html
230 # Multiple trait bounds
232 You’ve seen that you can bound a generic type parameter with a trait:
235 fn foo<T: Clone>(x: T) {
240 If you need more than one bound, you can use `+`:
245 fn foo<T: Clone + Debug>(x: T) {
251 `T` now needs to be both `Clone` as well as `Debug`.
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
262 fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
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.
272 Rust has a solution, and it’s called a ‘`where` clause’:
277 fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
283 fn bar<T, K>(x: T, y: K) where T: Clone, K: Clone + Debug {
290 foo("Hello", "world");
291 bar("Hello", "world");
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
303 fn bar<T, K>(x: T, y: K)
313 This flexibility can add clarity in complex situations.
315 `where` is also more powerful than the simpler syntax. For example:
318 trait ConvertTo<Output> {
319 fn convert(&self) -> Output;
322 impl ConvertTo<i64> for i32 {
323 fn convert(&self) -> i64 { *self as i64 }
326 // can be called with T == i32
327 fn normal<T: ConvertTo<i64>>(x: &T) -> i64 {
331 // can be called with T == i64
333 // this is using ConvertTo as if it were "ConvertFrom<i32>"
334 where i32: ConvertTo<T> {
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`).
345 There’s one last feature of traits we should cover: default methods. It’s
346 easiest just to show an example:
352 fn baz(&self) { println!("We called baz."); }
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:
363 # fn baz(&self) { println!("We called baz."); }
367 impl Foo for UseDefault {
368 fn bar(&self) { println!("We called bar."); }
371 struct OverrideDefault;
373 impl Foo for OverrideDefault {
374 fn bar(&self) { println!("We called bar."); }
376 fn baz(&self) { println!("Override baz!"); }
379 let default = UseDefault;
380 default.baz(); // prints "We called baz."
382 let over = OverrideDefault;
383 over.baz(); // prints "Override baz!"
388 Sometimes, implementing a trait requires implementing another trait:
400 Implementors of `FooBar` must also implement `Foo`, like this:
406 # trait FooBar : Foo {
412 fn foo(&self) { println!("foo"); }
415 impl FooBar for Baz {
416 fn foobar(&self) { println!("foobar"); }
420 If we forget to implement `Foo`, Rust will tell us:
423 error: the trait `main::Foo` is not implemented for the type `main::Baz` [E0277]