Skip to content

Intermediate · Modules, crates, traits and generics · topic 28 of 50 · 30 min

Traits: shared behaviour

Explanation

A trait lists methods that a type promises to provide, like an interface in Java or C#. You implement a trait for a type with impl Trait for Type, and traits can have default methods. Many standard traits (Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default) can be derived, and Display lets {} print your type.

Why traits?

Different types often share an ability. A train ticket, a movie ticket and a flight ticket all have a price and can print a summary. A trait is a named set of methods that types can promise to provide. Code can then work with "anything that has this ability" without knowing the exact type. This is Rust's main tool for shared behaviour; Rust has no class inheritance.

rust
trait Ticket {
    fn price(&self) -> u32;
    fn summary(&self) -> String;
}

struct Train { from: String, to: String, fare: u32 }
struct Movie { title: String, seats: u32 }

impl Ticket for Train {
    fn price(&self) -> u32 { self.fare }
    fn summary(&self) -> String { format!("Train {} to {}", self.from, self.to) }
}

impl Ticket for Movie {
    fn price(&self) -> u32 { self.seats * 250 }
    fn summary(&self) -> String { format!("{} x{}", self.title, self.seats) }
}
Each type implements the same two methods in its own way.
Think of it like this: A trait is like the "FSSAI licensed" mark on food packets. A dhaba, a bakery and a sweet shop are completely different businesses, but the mark tells you each one has promised to follow the same food-safety rules. In the same way, Train and Movie are different types, but impl Ticket promises both have price and summary.

Default methods

A trait can give a method a default body. Types get it for free and can override it. Default methods may call the other methods of the trait.

rust
trait Ticket {
    fn price(&self) -> u32;
    fn summary(&self) -> String;
    fn with_gst(&self) -> u32 {          // default method
        self.price() + self.price() * 18 / 100
    }
}
Train and Movie now both have with_gst() without writing it.

Using a trait in a function

rust
fn print_receipt(t: &impl Ticket) {
    println!("{}: Rs {}", t.summary(), t.with_gst());
}
&impl Ticket means "a reference to any type that implements Ticket". More on this in the next topic.

Display: making {} work

Display is a standard trait that controls how {} prints a value. You implement its fmt method and use write! to produce the text.

rust
use std::fmt;

struct Rupees(u64);   // paise

impl fmt::Display for Rupees {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Rs {}.{:02}", self.0 / 100, self.0 % 100)
    }
}

fn main() {
    let r = Rupees(12_345);
    println!("{r}");                 // Rs 123.45
    let s = r.to_string();           // to_string comes free with Display
    println!("{}", s.len());         // 9
}
Implementing Display also gives you .to_string().

Derivable traits

TraitGives youExample use
Debug{:?} printingDebugging, logs
Clone / Copy.clone() / implicit copiesDuplicating values
PartialEq / Eq== and !=Comparing two orders
PartialOrd / Ord<, >, sort()Sorting a Vec of your type
HashUse as a HashMap key (with Eq)A struct as a key
DefaultType::default() with zero/empty fieldsStarting settings
rust
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
struct RollNo(u32);
derive writes the implementations from the fields. Ord compares fields in order.

The orphan rule, in plain words

You may implement a trait for a type only if the trait or the type (or both) is defined in your own crate. You can implement Display for your Rupees, or your own Ticket trait for String, but not the standard Display for the standard Vec. This stops two crates from giving conflicting implementations of the same pair.

Common mistakes

  • Forgetting to bring a trait into scope. Trait methods only work when the trait is imported with use; the compiler tells you which one.
  • Missing a required method in impl: "not all trait items implemented". Add the method.
  • Deriving Copy on a type with a String field. Not allowed; derive Clone only.
  • Implementing a foreign trait for a foreign type (orphan rule). Wrap the type in your own newtype struct first.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Traits: shared behaviour · 1/7

trait = shared ability

A trait is a named set of methods that types promise to provide. It is like an interface in other languages.

Prefer a video by a teacher? Find free videos on “rust traits tutorial impl trait display derive” on YouTube ↗

In real life: Notifications through many channels

A bank sends alerts by SMS, email and push notification. Each channel is a struct that implements a Notifier trait with a send method. The alert code takes anything that implements Notifier, so adding WhatsApp later means writing one new impl, not changing the alert logic.

rust
trait Notifier {
    fn send(&self, msg: &str) -> bool;
}

struct Sms { phone: String }
struct Email { address: String }

impl Notifier for Sms {
    fn send(&self, msg: &str) -> bool {
        println!("SMS to {}: {msg}", self.phone);
        true
    }
}

impl Notifier for Email {
    fn send(&self, msg: &str) -> bool {
        println!("Email to {}: {msg}", self.address);
        true
    }
}

fn alert(channel: &impl Notifier) {
    channel.send("Rs 5,000 debited from your account");
}

fn main() {
    alert(&Sms { phone: String::from("98xxxxxx10") });
    alert(&Email { address: String::from("user@example.com") });
}
The same alert function works with any channel.

Practice (0/3 done)

Task 1 · Question

What does this print? trait Greet { fn name(&self) -> String; fn hello(&self) -> String { format!("Hello, {}!", self.name()) } } struct P; impl Greet for P { fn name(&self) -> String { "Riya".to_string() } } fn main() { println!("{}", P.hello()); }

Task 2 · Question

You want to sort a Vec of your struct with v.sort(). Which traits must it have?

Task 3 · Practice

Define a trait Shape with fn area(&self) -> f64 and a default fn describe(&self) -> String that uses area. Implement it for Circle and Rectangle. Implement Display for Rectangle so println!("{r}") prints "Rectangle 3 x 4". Write fn largest_area(a: &impl Shape, b: &impl Shape) -> f64.

Quick quiz

Question 1

1.What is a trait?

Question 2

2.What do you get for free by implementing Display?

Question 3

3.Which derive lets you compare two values with ==?

Question 4

4.Can you implement std's Display for std's Vec<u8> in your crate?

Question 5

5.Does Rust support class inheritance?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What is a trait?

Answer: A set of methods types can promise to implement

Traits describe shared behaviour.

What do you get for free by implementing Display?

Answer: to_string()

Any Display type gets to_string() through a blanket implementation.

Which derive lets you compare two values with ==?

Answer: PartialEq

PartialEq provides == and !=.

Can you implement std's Display for std's Vec<u8> in your crate?

Answer: No, because of the orphan rule

Neither the trait nor the type is yours. Wrap Vec in a newtype instead.

Does Rust support class inheritance?

Answer: No; it uses traits for shared behaviour

Rust uses traits and composition instead of inheritance.

What does -> impl Iterator<Item = u32> promise?Related

Answer: The function returns some single concrete type that is an iterator of u32

impl Trait in return position hides one concrete type.

From Generics and trait bounds

Which type is Copy?Related

Answer: char

char is a simple stack-only type, so it is Copy.

From Copy and Clone

Why must dyn Trait be behind a pointer like Box?Related

Answer: Its size is not known at compile time

Different implementing types have different sizes.

From Trait objects and dynamic dispatch

Which trait gives an error a human-readable message for {}?Related

Answer: Display

Display controls how {} prints a value.

From Custom error types

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 2 more topics in “Modules, crates, traits and generics” before its exam: Generics and trait bounds, Trait objects and dynamic dispatch.
  2. Module 7 exam: Modules, crates, traits and generics · 13 questions · 25 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, Closures, iterators and smart pointers. 50 topics left in the course before the final exam.

← Previous: Packages, crates and modules