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
trait{:rust}keyword 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_author{:rust}method is added to theSummary{:rust}trait, which is implemented by bothNewsArticle{:rust}andTweet{:rust}structs
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 Summary{:rust}is used as a parameter type for thenotify{:rust}function- This allows the function to accept any type that implements the
Summary{:rust}trait
Trait Bound Syntax
pub fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
fn main() {
// --snip--
notify(&tweet);
notify(&article);
}T: Summary{:rust}is a trait bound, which specifies thatT{:rust}must implement theSummary{:rust}trait
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 + Display{:rust}specifies thatT{:rust}must implement both theSummary{:rust}andDisplay{:rust}traits
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 {}where{:rust}clause 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 Summary{:rust}is used as a return type, which specifies that the function will return a type that implements theSummary{:rust}trait
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();
}PartialOrd{:rust}andDisplay{:rust}traits are implemented for thePair{:rust}struct, which allows thecmp_display{:rust}method 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)
}
}