Skip to content

Traits

A trait is essentially an interface. It defines functionality that can then be shared with other types.

Defining a trait

You do not have to provide a function body when defining a trait. If not, the implementation must be done on each type that inherits this trait. It is possible to define a default behavior and then later override it. Trait methods can also call other trait methods, even if they do not have default implementations. Those functions called without default implentations will then need to be defined in the inheriting type.

rust
pub trait Summary {
    fn summarize(&self) -> String;
}

pub trait Summary {
    fn summarize(&self) -> String {
        String::from("(Read more...)")
    }
}

pub trait Summary {
    fn summarize_author(&self) -> String;

    fn summarize(&self) -> String {
        format!("(Read more from {}...)", self.summarize_author())
    }
}

Implementing a trait

To implement a trait, you use the impl <trait> for <struct> syntax.

rust
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)
    }
}

pub struct Tweet {
    pub username: String,
    pub content: String,
    pub reply: bool,
    pub retweet: bool,
}

impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}

Traits as parameters

You can also use traits as you would types if you only want to call functions defined in the trait.

rust
pub fn notify(item: &impl Summary) {
    println!("Breaking news! {}", item.summarize());
}
// Or
pub fn notify<T: Summary>(item: &T) {
    println!("Breaking news! {}", item.summarize());
}

// With multiple traits
pub fn notify(item: &(impl Summary + Display)) {
    // do something
}
// Or
pub fn notify<T: Summary + Display>(item: &T) {
    // do something
}

// With where clause
fn some_function<T, U>(t: &T, u: &U) -> i32
    where T: Display + Clone,
          U: Clone + Debug
{
    // do something
}

Returning types that implement traits

You can only use a trait as a return value if you are returning a single type.

rust
fn returns_summarizable() -> impl Summary {
    Tweet {
        username: String::from("horse_ebooks"),
        content: String::from(
            "of course, as you probably already know, people",
        ),
        reply: false,
        retweet: false,
    }
}