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:
trait Summary { ... }defines the trait. Inside are method signatures: the name, parameters and return type, ending with;instead of a body. These are the required methods.impl Summary for Article { ... }implements the trait for a type: "an Article can summarize itself, and here's how". It looks like the impl blocks from the methods module, but it may only contain the trait's methods, with exactly the trait's signatures.- Calling a trait method looks like calling any method:
a.summarize().
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
- Forgetting a required method. Every method without a body must be implemented:
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`
- A different signature. Writing
fn summarize(self)when the trait says&selfgiveserror[E0053]: method `summarize` has an incompatible type for trait. Copy the signature from the trait exactly. - Extra methods in the trait impl.
impl Summary for Postmay only containSummary's methods. Adding afn extra(&self)there giveserror[E0407]: method `extra` is not a member of trait `Summary`. Put the type's other methods in a plainimpl Postblock.
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:
Coin: 10 points, or 50 ifgoldis true.Gem: 25 points per carat.