2014-12-02 08:20:48 -06:00
% Traits
2015-06-08 19:32:29 -05:00
A trait is a language feature that tells the Rust compiler about
functionality a type must provide.
2015-12-08 16:38:55 -06:00
Recall the `impl` keyword, used to call a function with [method
syntax][methodsyntax]:
2014-12-02 08:20:48 -06:00
2015-04-18 16:21:26 -05:00
```rust
2014-12-02 08:20:48 -06:00
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl Circle {
fn area(& self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}
```
2015-04-18 16:21:26 -05:00
[methodsyntax]: method-syntax.html
2015-12-08 16:38:55 -06:00
Traits are similar, except that we first define a trait with a method
2015-12-08 16:56:42 -06:00
signature, then implement the trait for a type. In this example, we implement the trait `HasArea` for `Circle` :
2014-12-02 08:20:48 -06:00
2015-04-18 16:21:26 -05:00
```rust
2014-12-02 08:20:48 -06:00
struct Circle {
x: f64,
y: f64,
radius: f64,
}
trait HasArea {
fn area(& self) -> f64;
}
impl HasArea for Circle {
fn area(& self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}
```
As you can see, the `trait` block looks very similar to the `impl` block,
2015-04-18 16:21:26 -05:00
but we don’ t define a body, just a type signature. When we `impl` a trait,
2014-12-02 08:20:48 -06:00
we use `impl Trait for Item` , rather than just `impl Item` .
2015-10-22 23:45:44 -05:00
## Trait bounds on generic functions
2015-07-25 06:52:14 -05:00
Traits are useful because they allow a type to make certain promises about its
2015-10-22 23:45:44 -05:00
behavior. Generic functions can exploit this to constrain, or [bound][bounds], the types they
2015-07-25 06:52:14 -05:00
accept. Consider this function, which does not compile:
2014-12-02 08:20:48 -06:00
2015-10-22 23:45:44 -05:00
[bounds]: glossary.html#bounds
2015-04-18 16:21:26 -05:00
```rust,ignore
2014-12-02 08:20:48 -06:00
fn print_area< T > (shape: T) {
println!("This shape has an area of {}", shape.area());
}
```
Rust complains:
```text
2015-06-01 16:06:51 -05:00
error: no method named `area` found for type `T` in the current scope
2014-12-02 08:20:48 -06:00
```
2015-04-18 16:21:26 -05:00
Because `T` can be any type, we can’ t be sure that it implements the `area`
2015-10-22 23:45:44 -05:00
method. But we can add a trait bound to our generic `T` , ensuring
2014-12-02 08:20:48 -06:00
that it does:
2015-04-18 16:21:26 -05:00
```rust
2014-12-02 08:20:48 -06:00
# trait HasArea {
# fn area(&self) -> f64;
# }
fn print_area< T: HasArea > (shape: T) {
println!("This shape has an area of {}", shape.area());
}
```
2015-07-25 06:52:14 -05:00
The syntax `<T: HasArea>` means “any type that implements the `HasArea` trait.”
2014-12-02 08:20:48 -06:00
Because traits define function type signatures, we can be sure that any type
which implements `HasArea` will have an `.area()` method.
2015-04-18 16:21:26 -05:00
Here’ s an extended example of how this works:
2014-12-02 08:20:48 -06:00
2015-04-18 16:21:26 -05:00
```rust
2014-12-02 08:20:48 -06:00
trait HasArea {
fn area(& self) -> f64;
}
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl HasArea for Circle {
fn area(& self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}
struct Square {
x: f64,
y: f64,
side: f64,
}
impl HasArea for Square {
fn area(& self) -> f64 {
self.side * self.side
}
}
fn print_area< T: HasArea > (shape: T) {
println!("This shape has an area of {}", shape.area());
}
fn main() {
let c = Circle {
x: 0.0f64,
y: 0.0f64,
radius: 1.0f64,
};
let s = Square {
x: 0.0f64,
y: 0.0f64,
side: 1.0f64,
};
print_area(c);
print_area(s);
}
```
This program outputs:
```text
This shape has an area of 3.141593
This shape has an area of 1
```
2015-04-18 16:21:26 -05:00
As you can see, `print_area` is now generic, but also ensures that we have
passed in the correct types. If we pass in an incorrect type:
2014-12-02 08:20:48 -06:00
2015-04-18 16:21:26 -05:00
```rust,ignore
2015-01-13 09:40:18 -06:00
print_area(5);
2014-12-02 08:20:48 -06:00
```
We get a compile-time error:
```text
2015-05-27 18:20:32 -05:00
error: the trait `HasArea` is not implemented for the type `_` [E0277]
2014-12-02 08:20:48 -06:00
```
2015-10-22 23:45:44 -05:00
## Trait bounds on generic structs
2015-07-19 06:22:39 -05:00
2015-10-22 23:45:44 -05:00
Your generic structs can also benefit from trait bounds. All you need to
do is append the bound when you declare type parameters. Here is a new
2015-07-19 06:22:39 -05:00
type `Rectangle<T>` and its operation `is_square()` :
```rust
struct Rectangle< T > {
x: T,
y: T,
width: T,
height: T,
}
impl< T: PartialEq > Rectangle< T > {
fn is_square(& self) -> bool {
self.width == self.height
}
}
fn main() {
let mut r = Rectangle {
x: 0,
y: 0,
width: 47,
height: 47,
};
assert!(r.is_square());
r.height = 42;
assert!(!r.is_square());
}
```
`is_square()` needs to check that the sides are equal, so the sides must be of
a type that implements the [`core::cmp::PartialEq`][PartialEq] trait:
```ignore
impl< T: PartialEq > Rectangle< T > { ... }
```
Now, a rectangle can be defined in terms of any type that can be compared for
equality.
[PartialEq]: ../core/cmp/trait.PartialEq.html
2015-07-19 09:23:40 -05:00
Here we defined a new struct `Rectangle` that accepts numbers of any
precision—really, objects of pretty much any type—as long as they can be
compared for equality. Could we do the same for our `HasArea` structs, `Square`
and `Circle` ? Yes, but they need multiplication, and to work with that we need
to know more about [operator traits][operators-and-overloading].
2015-07-19 06:22:39 -05:00
2015-07-19 09:23:40 -05:00
[operators-and-overloading]: operators-and-overloading.html
2015-07-19 06:22:39 -05:00
# Rules for implementing traits
2015-04-18 16:21:26 -05:00
So far, we’ ve only added trait implementations to structs, but you can
implement a trait for any type. So technically, we _could_ implement `HasArea`
for `i32` :
2014-12-02 08:20:48 -06:00
2015-04-18 16:21:26 -05:00
```rust
2014-12-02 08:20:48 -06:00
trait HasArea {
fn area(& self) -> f64;
}
2015-01-13 09:40:18 -06:00
impl HasArea for i32 {
2014-12-02 08:20:48 -06:00
fn area(& self) -> f64 {
println!("this is silly");
*self as f64
}
}
2015-01-13 09:40:18 -06:00
5.area();
2014-12-02 08:20:48 -06:00
```
It is considered poor style to implement methods on such primitive types, even
though it is possible.
2015-07-25 06:52:14 -05:00
This may seem like the Wild West, but there are two restrictions around
2015-04-18 16:21:26 -05:00
implementing traits that prevent this from getting out of hand. The first is
that if the trait isn’ t defined in your scope, it doesn’ t apply. Here’ s an
example: the standard library provides a [`Write`][write] trait which adds
extra functionality to `File` s, for doing file I/O. By default, a `File`
won’ t have its methods:
2014-12-02 08:20:48 -06:00
2015-04-18 16:21:26 -05:00
[write]: ../std/io/trait.Write.html
2014-12-02 08:20:48 -06:00
2015-04-18 16:21:26 -05:00
```rust,ignore
2015-11-01 13:40:20 -06:00
let mut f = std::fs::File::open("foo.txt").expect("Couldn’ t open foo.txt");
2015-05-17 23:18:29 -05:00
let buf = b"whatever"; // byte string literal. buf: &[u8; 8]
let result = f.write(buf);
2015-04-25 09:46:34 -05:00
# result.unwrap(); // ignore the error
2014-12-02 08:20:48 -06:00
```
2015-04-18 16:21:26 -05:00
Here’ s the error:
2014-12-02 08:20:48 -06:00
```text
2015-04-18 16:21:26 -05:00
error: type `std::fs::File` does not implement any method in scope named `write`
2015-05-17 23:18:29 -05:00
let result = f.write(buf);
^~~~~~~~~~
2015-04-18 16:21:26 -05:00
```
2014-12-02 08:20:48 -06:00
2015-04-18 16:21:26 -05:00
We need to `use` the `Write` trait first:
2014-12-02 08:20:48 -06:00
2015-04-18 16:21:26 -05:00
```rust,ignore
use std::io::Write;
2014-12-02 08:20:48 -06:00
2015-11-01 13:40:20 -06:00
let mut f = std::fs::File::open("foo.txt").expect("Couldn’ t open foo.txt");
2015-05-17 23:18:29 -05:00
let buf = b"whatever";
let result = f.write(buf);
2015-04-25 09:46:34 -05:00
# result.unwrap(); // ignore the error
2014-12-02 08:20:48 -06:00
```
2015-04-18 16:21:26 -05:00
This will compile without error.
2015-05-15 11:14:39 -05:00
This means that even if someone does something bad like add methods to `i32` ,
2015-04-18 16:21:26 -05:00
it won’ t affect you, unless you `use` that trait.
2014-12-02 08:20:48 -06:00
2015-06-01 16:06:23 -05:00
There’ s one more restriction on implementing traits: either the trait, or the
type you’ re writing the `impl` for, must be defined by you. So, we could
2015-04-18 16:21:26 -05:00
implement the `HasArea` type for `i32` , because `HasArea` is in our code. But
2015-06-01 16:07:42 -05:00
if we tried to implement `ToString` , a trait provided by Rust, for `i32` , we could
2015-04-18 16:21:26 -05:00
not, because neither the trait nor the type are in our code.
2014-12-02 08:20:48 -06:00
One last thing about traits: generic functions with a trait bound use
2015-04-18 16:21:26 -05:00
‘ monomorphization’ (mono: one, morph: form), so they are statically dispatched.
What’ s that mean? Check out the chapter on [trait objects][to] for more details.
[to]: trait-objects.html
2015-01-13 14:42:38 -06:00
2015-04-18 16:21:26 -05:00
# Multiple trait bounds
2015-03-31 18:45:09 -05:00
You’ ve seen that you can bound a generic type parameter with a trait:
```rust
fn foo< T: Clone > (x: T) {
x.clone();
}
```
If you need more than one bound, you can use `+` :
```rust
use std::fmt::Debug;
fn foo< T: Clone + Debug > (x: T) {
x.clone();
println!("{:?}", x);
}
```
`T` now needs to be both `Clone` as well as `Debug` .
2015-04-18 16:21:26 -05:00
# Where clause
2015-02-09 17:00:50 -06:00
Writing functions with only a few generic types and a small number of trait
2015-04-18 16:21:26 -05:00
bounds isn’ t too bad, but as the number increases, the syntax gets increasingly
2015-02-09 17:00:50 -06:00
awkward:
2015-05-18 13:56:00 -05:00
```rust
2015-02-09 17:00:50 -06:00
use std::fmt::Debug;
fn foo< T: Clone , K: Clone + Debug > (x: T, y: K) {
x.clone();
y.clone();
println!("{:?}", y);
}
```
The name of the function is on the far left, and the parameter list is on the
far right. The bounds are getting in the way.
2015-04-18 16:21:26 -05:00
Rust has a solution, and it’ s called a ‘ `where` clause’ :
2015-02-09 17:00:50 -06:00
2015-05-18 13:56:00 -05:00
```rust
2015-02-09 17:00:50 -06:00
use std::fmt::Debug;
fn foo< T: Clone , K: Clone + Debug > (x: T, y: K) {
x.clone();
y.clone();
println!("{:?}", y);
}
fn bar< T , K > (x: T, y: K) where T: Clone, K: Clone + Debug {
x.clone();
y.clone();
println!("{:?}", y);
}
fn main() {
foo("Hello", "world");
2015-05-28 17:43:39 -05:00
bar("Hello", "world");
2015-02-09 17:00:50 -06:00
}
```
`foo()` uses the syntax we showed earlier, and `bar()` uses a `where` clause.
All you need to do is leave off the bounds when defining your type parameters,
and then add `where` after the parameter list. For longer lists, whitespace can
be added:
2015-05-18 13:56:00 -05:00
```rust
2015-02-09 17:00:50 -06:00
use std::fmt::Debug;
fn bar< T , K > (x: T, y: K)
where T: Clone,
K: Clone + Debug {
x.clone();
y.clone();
println!("{:?}", y);
}
```
This flexibility can add clarity in complex situations.
`where` is also more powerful than the simpler syntax. For example:
2015-05-18 13:56:00 -05:00
```rust
2015-02-09 17:00:50 -06:00
trait ConvertTo< Output > {
fn convert(& self) -> Output;
}
impl ConvertTo< i64 > for i32 {
2015-02-17 21:00:20 -06:00
fn convert(& self) -> i64 { *self as i64 }
2015-02-09 17:00:50 -06:00
}
// can be called with T == i32
fn normal< T: ConvertTo < i64 > >(x: & T) -> i64 {
x.convert()
}
// can be called with T == i64
fn inverse< T > () -> T
2015-09-03 00:23:00 -05:00
// this is using ConvertTo as if it were "ConvertTo< i64 > "
2015-02-09 17:00:50 -06:00
where i32: ConvertTo< T > {
2015-06-15 11:42:07 -05:00
42.convert()
2015-02-09 17:00:50 -06:00
}
```
This shows off the additional feature of `where` clauses: they allow bounds
2015-12-08 16:38:55 -06:00
on the left-hand side not only of type parameters `T` , but also of types (`i32` in this case). In this example, `i32` must implement
2015-11-01 12:28:51 -06:00
`ConvertTo<T>` . Rather than defining what `i32` is (since that's obvious), the
2015-12-08 16:38:55 -06:00
`where` clause here constrains `T` .
2015-02-09 17:00:50 -06:00
2015-07-25 06:52:14 -05:00
# Default methods
2015-03-04 16:13:49 -06:00
2015-12-08 16:38:55 -06:00
A default method can be added to a trait definition if it is already known how a typical implementor will define a method. For example, `is_invalid()` is defined as the opposite of `is_valid()` :
2015-03-04 16:13:49 -06:00
```rust
trait Foo {
2015-07-25 11:18:47 -05:00
fn is_valid(& self) -> bool;
2015-03-04 16:13:49 -06:00
2015-07-25 11:18:47 -05:00
fn is_invalid(& self) -> bool { !self.is_valid() }
2015-03-04 16:13:49 -06:00
}
```
2015-12-08 16:38:55 -06:00
Implementors of the `Foo` trait need to implement `is_valid()` but not `is_invalid()` due to the added default behavior. This default behavior can still be overridden as in:
2015-03-04 16:13:49 -06:00
```rust
# trait Foo {
2015-07-25 11:18:47 -05:00
# fn is_valid(&self) -> bool;
#
# fn is_invalid(&self) -> bool { !self.is_valid() }
2015-03-04 16:13:49 -06:00
# }
struct UseDefault;
impl Foo for UseDefault {
2015-07-25 11:18:47 -05:00
fn is_valid(& self) -> bool {
println!("Called UseDefault.is_valid.");
true
}
2015-03-04 16:13:49 -06:00
}
struct OverrideDefault;
impl Foo for OverrideDefault {
2015-07-25 11:18:47 -05:00
fn is_valid(& self) -> bool {
println!("Called OverrideDefault.is_valid.");
true
}
2015-03-04 16:13:49 -06:00
2015-07-25 11:18:47 -05:00
fn is_invalid(& self) -> bool {
println!("Called OverrideDefault.is_invalid!");
2015-12-08 16:38:55 -06:00
true // overrides the expected value of is_invalid()
2015-07-25 11:18:47 -05:00
}
2015-03-04 16:13:49 -06:00
}
let default = UseDefault;
2015-07-25 11:18:47 -05:00
assert!(!default.is_invalid()); // prints "Called UseDefault.is_valid."
2015-03-04 16:13:49 -06:00
let over = OverrideDefault;
2015-07-25 11:18:47 -05:00
assert!(over.is_invalid()); // prints "Called OverrideDefault.is_invalid!"
2015-03-04 16:13:49 -06:00
```
2015-04-18 16:21:26 -05:00
# Inheritance
Sometimes, implementing a trait requires implementing another trait:
```rust
trait Foo {
fn foo(&self);
}
trait FooBar : Foo {
fn foobar(&self);
}
```
Implementors of `FooBar` must also implement `Foo` , like this:
```rust
# trait Foo {
# fn foo(&self);
# }
# trait FooBar : Foo {
# fn foobar(&self);
# }
struct Baz;
impl Foo for Baz {
fn foo(& self) { println!("foo"); }
}
impl FooBar for Baz {
fn foobar(& self) { println!("foobar"); }
}
```
If we forget to implement `Foo` , Rust will tell us:
```text
error: the trait `main::Foo` is not implemented for the type `main::Baz` [E0277]
```
2015-10-08 00:10:56 -05:00
# Deriving
2015-12-08 16:38:55 -06:00
Implementing traits like `Debug` and `Default` repeatedly can become
2015-10-08 00:10:56 -05:00
quite tedious. For that reason, Rust provides an [attribute][attributes] that
allows you to let Rust automatically implement traits for you:
```rust
#[derive(Debug)]
struct Foo;
fn main() {
println!("{:?}", Foo);
}
```
[attributes]: attributes.html
However, deriving is limited to a certain set of traits:
2015-10-08 00:27:26 -05:00
- [`Clone` ](../core/clone/trait.Clone.html )
- [`Copy` ](../core/marker/trait.Copy.html )
- [`Debug` ](../core/fmt/trait.Debug.html )
- [`Default` ](../core/default/trait.Default.html )
- [`Eq` ](../core/cmp/trait.Eq.html )
- [`Hash` ](../core/hash/trait.Hash.html )
- [`Ord` ](../core/cmp/trait.Ord.html )
- [`PartialEq` ](../core/cmp/trait.PartialEq.html )
- [`PartialOrd` ](../core/cmp/trait.PartialOrd.html )