🚀
10. Traits

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
  • trait 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 method is added to the Summary trait, which is implemented by both NewsArticle and Tweet 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 is used as a parameter type for the notify function
  • This allows the function to accept any type that implements the Summary 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 is a trait bound, which specifies that T must implement the Summary 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 specifies that T must implement both the Summary and Display 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 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 is used as a return type, which specifies that the function will return a type that implements the Summary 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 and Display traits are implemented for the Pair struct, which allows the cmp_display 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)
    }
}

© 2024 Driptanil Datta.All rights reserved

Made with Love ❤️

Last updated on Mon Oct 20 2025