RustArena

Step 1 of 6

Defining and implementing a trait

A trait describes behavior that several types can share: a list of methods a type promises to have. You've been using traits all along without writing one. {:?} works because a type implements the Debug trait, == works through PartialEq, and in the generics module you wrote bounds like T: PartialOrd. Now you'll write your own.

A news app shows a one-line preview for very different kinds of content:

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

struct Article {
    title: String,
    author: String,
}

struct Post {
    user: String,
    text: String,
}

impl Summary for Article {
    fn summarize(&self) -> String {
        format!("{}, by {}", self.title, self.author)
    }
}

impl Summary for Post {
    fn summarize(&self) -> String {
        format!("@{}: {}", self.user, self.text)
    }
}

fn main() {
    let a = Article { title: String::from("Rust 2024 is out"), author: String::from("Ana") };
    let p = Post { user: String::from("ferris"), text: String::from("click clack") };
    println!("{}", a.summarize());
    println!("{}", p.summarize());
}
Rust 2024 is out, by Ana
@ferris: click clack

Piece by piece:

The trait decides what every implementing type can do; each type decides how. Why not just give each struct its own summarize method? You could, but nothing would connect them. With a trait, you can write one function that accepts "anything that implements Summary" (step 3), and the compiler checks that every type really has the method.

Your trait on types you didn't write

You can implement your own trait for built-in types too:

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

impl Summary for i32 {
    fn summarize(&self) -> String {
        format!("the number {}", self)
    }
}

impl Summary for Vec<String> {
    fn summarize(&self) -> String {
        format!("a list of {} items", self.len())
    }
}

fn main() {
    let tags = vec![String::from("rust"), String::from("crab")];
    println!("{}", 42.summarize());
    println!("{}", tags.summarize());
}
the number 42
a list of 2 items

There's one limit, the orphan rule: you may write impl SomeTrait for SomeType only if the trait or the type (or both) is defined in your crate. impl Summary for i32 is fine because Summary is yours. Implementing the standard library's Display for Vec<String> is not allowed, because neither is yours. (The advanced traits module shows the standard workaround.)

Common mistakes

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

struct Post {
    text: String,
}

impl Summary for Post {
    fn summarize(&self) -> String {
        self.text.clone()
    }
}

fn main() {
    let p = Post { text: String::from("hi") };
    println!("{}", p.summarize());
}
error[E0046]: not all trait items implemented, missing: `short`

Your task

Fill in the blanks. Define a trait Describe with one method, and implement it for Book and Movie, so the program prints:

Book: Dune (1965)
Movie: Alien, 117 min

Challenge 2

The Describe trait now has a third type, Podcast. Implement Describe for it so that describe() returns Podcast: <name> (<episodes> episodes), for example Podcast: Rustacean Station (120 episodes). With exactly one episode it should say 1 episode, not 1 episodes.

Challenge 3

A game awards points for picking things up. Starting from the two structs, write a trait Points with one method fn points(&self) -> u32, and implement it:

Next: Default methods