Traits
pub struct NewsArticle {
pub headline: String,
pub author: String,
pub content: String,
}
impl NewsArticle {
fn summarize(&self) -> String {
format!("{} | by {}", self.headline, self.author)
}
}
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
impl Tweet {
fn summarize(&self) -> String {
format!("{}, @ {}", self.content, self.username)
}
}
fn main() {
let tweet = Tweet {
username: String::from("@johndoe"),
content: String::from("Hello, World !"),
reply: false,
retweet: false,
};
let article = NewsArticle {
headline: String::from("Breaking News"),
author: String::from("John Doe"),
content: String::from("This is a breaking news article"),
};
println!("1 new tweet: {}", tweet.summarize());
println!("New article available: {}", article.summarize());
}$ cargo run
1 new tweet: @johndoe, by Hello, World !
New article available: Breaking News, by John Doe (This is a breaking news article)- Traits are used to define shared behavior in an abstract way
traitkeyword is used to define a trait
pub struct NewsArticle {
pub headline: String,
pub author: String,
pub content: String,
}
impl Summary for NewsArticle {}
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}, by {}", self.username, self.content)
}
}
pub trait Summary {
fn summarize(&self) -> String {
String::from("(Read more...)")
}
}
fn main() {
// --snip--
}
$ cargo run
1 new tweet: @johndoe, by Hello, World !
New article available: (Read more...)pub trait Summary {
fn summarize_author(&self) -> String;
fn summarize(&self) -> String {
format!("(Read more from {}...)", self.summarize_author())
}
}
impl Summary for NewsArticle {
fn summarize_author(&self) -> String {
format!("@{}", self.author)
}
}
impl Summary for Tweet {
fn summarize_author(&self) -> String {
format!("@{}", self.username)
}
fn summarize(&self) -> String {
format!("{}, by {}", self.username, self.content)
}
}$ cargo run
1 new tweet: @johndoe, by Hello, World !
New article available: (Read more from @John Doe...)summarize_authormethod is added to theSummarytrait, which is implemented by bothNewsArticleandTweetstructs
Traits as Parameters
pub fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
fn main() {
// --snip--
notify(&tweet);
notify(&article);
}$ cargo run
Breaking news! @johndoe, by Hello, World !
Breaking news! (Read more from @John Doe...)impl Summaryis used as a parameter type for thenotifyfunction- This allows the function to accept any type that implements the
Summarytrait
Trait Bound Syntax
pub fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
fn main() {
// --snip--
notify(&tweet);
notify(&article);
}T: Summaryis a trait bound, which specifies thatTmust implement theSummarytrait
Multiple Trait Bounds
pub fn notify<T: Summary + Display>(item: &T) {
println!("Breaking news! {}", item.summarize());
println!("Breaking news! {}", item);
}
fn main() {
// --snip--
notify(&tweet);
notify(&article);
}T: Summary + Displayspecifies thatTmust implement both theSummaryandDisplaytraits
Where Clauses
- Specifing multiple trait bounds could hinder code readablity, like
fn some_function<T: Display + Clone, U : Clone + Debug>(t: &T, u: & U) -> i32 {}whereclause is used to specify trait bounds, which makes the function signature more readable
pub fn notify<T>(item: &T)
where T: Summary + Display,
U: Clone + Debug
{
println!("Breaking news! {}", item.summarize());
println!("Breaking news! {}", item);
}Returning Types that Implement Traits
fn returns_summarizable() -> impl Summary {
Tweet {
username: String::from("@johndoe"),
content: String::from("Hello, World !"),
reply: false,
retweet: false,
}
}
fn main() {
let tweet = returns_summarizable();
println!("1 new tweet: {}", tweet.summarize());
}$ cargo run
1 new tweet: @johndoe, by Hello, World !impl Summaryis used as a return type, which specifies that the function will return a type that implements theSummarytrait
Conditional Implement Methods
- Methods can be implemented conditionally based on trait bounds
use std::fmt::Display;
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);
}
}
}
fn main() {
let pair = Pair::new(5, 10);
pair.cmp_display();
}PartialOrdandDisplaytraits are implemented for thePairstruct, which allows thecmp_displaymethod to be called
Blanket Implementations
- Implementing a trait for any type that implements another trait
impl<T: Display> ToString for T {
fn to_string(&self) -> String {
format!("{}", self)
}
}