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-04-18 16:21:26 -05:00
|
|
|
|
Do you remember 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
|
|
|
|
|
|
2014-12-02 08:20:48 -06:00
|
|
|
|
Traits are similar, except that we define a trait with just the method
|
|
|
|
|
signature, then implement the trait for that struct. Like this:
|
|
|
|
|
|
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-07-25 06:52:14 -05:00
|
|
|
|
## Traits bounds for generic functions
|
|
|
|
|
|
|
|
|
|
Traits are useful because they allow a type to make certain promises about its
|
|
|
|
|
behavior. Generic functions can exploit this to constrain the types they
|
|
|
|
|
accept. Consider this function, which does not compile:
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
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`
|
|
|
|
|
method. But we can add a ‘trait constraint’ 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-07-19 06:22:39 -05:00
|
|
|
|
## Traits bounds for generic structs
|
|
|
|
|
|
2015-07-25 06:52:14 -05:00
|
|
|
|
Your generic structs can also benefit from trait constraints. All you need to
|
|
|
|
|
do is append the constraint 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
|
|
|
|
|
let mut f = std::fs::File::open("foo.txt").ok().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-04-18 16:21:26 -05:00
|
|
|
|
let mut f = std::fs::File::open("foo.txt").ok().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
|
|
|
|
|
where the left-hand side is an arbitrary type (`i32` in this case), not just a
|
|
|
|
|
plain type parameter (like `T`).
|
|
|
|
|
|
2015-07-25 06:52:14 -05:00
|
|
|
|
# Default methods
|
2015-03-04 16:13:49 -06:00
|
|
|
|
|
2015-07-25 06:52:14 -05:00
|
|
|
|
If you already know how a typical implementor will define a method, you can
|
|
|
|
|
let your trait supply a default:
|
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-07-25 11:18:47 -05:00
|
|
|
|
Implementors of the `Foo` trait need to implement `is_valid()`, but they don’t
|
|
|
|
|
need to implement `is_invalid()`. They’ll get this default behavior. They can
|
2015-03-04 16:13:49 -06:00
|
|
|
|
override the default if they so choose:
|
|
|
|
|
|
|
|
|
|
```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!");
|
|
|
|
|
true // this implementation is a self-contradiction!
|
|
|
|
|
}
|
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]
|
|
|
|
|
```
|