Rust Traits
A Rust trait defines functionality of a set of types that implements the trait.
Define Traits
We can define new traits with the trait keyword
pub trait Summary {
fn summarize(&self) -> String;
}Implement Traits
Traits are implemented for types using the impl Trait for Type syntax:
pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}And then we can just call trait methods like normal methods. E.g. article.summarize().
We can’t implement external traits for external types because of Rust traits’ orphan rule. Though we can use extension traits to achieve the same effect.
Default Implementations
We can also provides a default implementation for a trait:
pub trait Summary {
fn summarize(&self) -> String {
String::from("(Read more...)")
}
}A default implementation can call other method in the same trait, even for those methods which does not have a default implementation.
Traits as Parameters
See: Argument-Position Impl Trait
We can also pass traits as a parameter to functions:
pub fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}The above syntax is a shorthand for the Trait bound syntax:
pub fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}We can also combine multiple traits with +:
pub fn notify(item: &(impl Summary + Display)) {or
pub fn notify<T: Summary + Display>(item: &T) {Where Syntax
where syntax can make function signature clearer when there are a lot of trait bound.
Instead of writing something like
fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 {we can write
fn some_function<T, U>(t: &T, u: &U) -> i32
where T: Display + Clone,
U: Clone + Debug
{Returning Types that Implement Traits
See: Return-Position impl Trait
We can use impl trait in return position to return a value of some type that implement a trait:
```rust
fn returns_summarizable() -> impl Summary {
Tweet { ... }
}Use Trait Bound to Conditionally Implement Methods
Just like C++ concept, we can use trait bound to conditionally implement methods:
struct Pair<T> {
x: T,
y: T,
}
impl<T> Pair<T> {
fn new(x: T, y: T) -> Self {
Self { x, y }
}
}
impl<T: Display + PartialOrd> Pair<T> {
fn cmp_display(&self) {
if self.x >= self.y {
println!("The largest member is x = {}", self.x);
} else {
println!("The largest member is y = {}", self.y);
}
}
}