A trait tells the Rust compiler about functionality a particular type has and can share with other types. We can use traits to define shared behavior in an abstract way. We can use trait bounds to specify that a generic type can be any type that has certain behavior.
The compiler is capable of providing basic implementations for some traits via
the `#[derive]` attribute. For more info, please visit [here](https://doc.rust-lang.org/book/appendix-03-derivable-traits.html).
2. šš
```rust,editable
// `Centimeters`, a tuple struct that can be compared
#[derive(PartialEq, PartialOrd)]
struct Centimeters(f64);
// `Inches`, a tuple struct that can be printed
#[derive(Debug)]
struct Inches(i32);
impl Inches {
fn to_centimeters(&self) -> Centimeters {
let &Inches(inches) = self;
Centimeters(inches as f64 * 2.54)
}
}
// ADD some attributes to make the code work!
// DON'T modify other codes!
struct Seconds(i32);
fn main() {
let _one_second = Seconds(1);
println!("One second looks like: {:?}", _one_second);
let _this_is_true = (_one_second == _one_second);
let _this_is_true = (_one_second > _one_second);
let foot = Inches(12);
println!("One foot equals {:?}", foot);
let meter = Centimeters(100.0);
let cmp =
if foot.to_centimeters() <meter{
"smaller"
} else {
"bigger"
};
println!("One foot is {} than one meter.", cmp);
}
```
### Operator
In Rust, many of the operators can be overloaded via traits. That is, some operators can be used to accomplish different tasks based on their input arguments. This is possible because operators are syntactic sugar for method calls. For example, the + operator in a + b calls the add method (as in a.add(b)). This add method is part of the Add trait. Hence, the + operator can be used by any implementor of the Add trait.
3. šš
```rust,editable
use std::ops;
// implement fn multiply to make the code work
// As mentiond above, `+` needs `T` to implement `std::ops::Add` Trait
// so, what about `*` ? You can find the answer here: https://doc.rust-lang.org/core/ops/
Instead of a concrete type for the item parameter, we specify the impl keyword and the trait name. This parameter accepts any type that implements the specified trait.
5. ššš
```rust,editable
// implement `fn summary` to make the code work
// fix the errors without removing any code line
trait Summary {
fn summarize(&self) -> String;
}
#[derive(Debug)]
struct Post {
title: String,
author: String,
content: String,
}
impl Summary for Post {
fn summarize(&self) -> String {
format!("The author of post {} is {}", self.title, self.author)
}
}
#[derive(Debug)]
struct Weibo {
username: String,
content: String,
}
impl Summary for Weibo {
fn summarize(&self) -> String {
format!("{} published a weibo {}", self.username, self.content)
}
}
fn main() {
let post = Post {
title: "Popular Rust".to_string(),
author: "Sunface".to_string(),
content: "Rust is awesome!".to_string(),
};
let weibo = Weibo {
username: "sunface".to_string(),
content: "Weibo seems to be worse than Tweet".to_string(),
};
summary(post);
summary(weibo);
println!("{:?}", post);
println!("{:?}", weibo);
}
// implement `fn summary` below
```
### Returning Types that Implement Traits
We can also use the impl Trait syntax in the return position to return a value of some type that implements a trait.
However, you can only use impl Trait if youāre returning a single type, using Trait Objects instead when you really need to return serveral types.
6. šš
```rust,editable
struct Sheep {}
struct Cow {}
trait Animal {
fn noise(&self) -> String;
}
impl Animal for Sheep {
fn noise(&self) -> String {
"baaaaah!".to_string()
}
}
impl Animal for Cow {
fn noise(&self) -> String {
"moooooo!".to_string()
}
}
// Returns some struct that implements Animal, but we don't know which one at compile time.
// FIX the erros here, you can make a fake random, or you can use trait object