Advanced

Traits

Define shared behavior that types can implement.

What you will see

42

About 2 min of reading

Try It

You will define a trait, implement it for a type, and use trait bounds on generic functions.

1

I define a trait with one method and implement it for a type. Calling the method through the instance runs the type's body.

trait Greet { fn greet(&self) -> String; } struct Dog { name: String } impl Greet for Dog { fn greet(&self) -> String { format!("Woof, I am {}", self.name) } } fn main() { let dog = Dog { name: "Rex".into() }; println!("{}", dog.greet()); }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Woof, I am Rex
2

I add a default method to the trait. A type that omits it still gets the default implementation.

trait Animal { fn name(&self) -> &str; fn describe(&self) -> String { format!("This is {}", self.name()) } } struct Cat { name: String } impl Animal for Cat { fn name(&self) -> &str { &self.name } } fn main() { let cat = Cat { name: "Mia".into() }; println!("{}", cat.describe()); }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
This is Mia
3

I write a generic function with a trait bound. The bound lets the function call the trait method on any type that implements it.

trait Greet { fn greet(&self) -> String; } struct Dog { name: String } impl Greet for Dog { fn greet(&self) -> String { format!("Woof, {}", self.name) } } fn greet_one(animal: &T) -> String { animal.greet() } fn main() { let dog = Dog { name: "Rex".into() }; println!("{}", greet_one(&dog)); }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Woof, Rex
4

I try to call a trait method on a type that does not implement the trait. The compiler rejects it, showing the trait must be implemented before use.

trait Greet { fn greet(&self) -> String; } struct Cat { name: String } fn main() { let cat = Cat { name: "Mia".into() }; println!("{}", cat.greet()); }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
COMPILE_ERROR
A type must implement the trait before you can call its methods.
5

I use a Display type in the format! macro. The Display trait provides the {} formatting used by println! and format!.

fn main() { let x = 42; println!("{}", x); }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
42

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

A type implements a trait; the method runs via dynamic dispatch.
1 Define a trait with methods
↓
2 A type implements the trait
↓
3 Call the trait method
↓
4 The type's body runs
Why this works
Trait definitions

A trait lists method signatures:

trait Shape {
    fn area(&self) -> f64;
}

Types implement the trait with an impl block. A type must implement every method to satisfy the trait.

Multiple trait bounds

Combine bounds:

fn describe<T: Greet + std::fmt::Display>(t: &T) {
    println!("{} says {}", t, t.greet());
}

+ chains multiple bounds. The type must implement all of them. Display lets values be formatted with {}.

Examples

Defining a trait

Declaring behavior

trait Greet {
    fn greet(&self) -> String;
}

struct Dog { name: String }

impl Greet for Dog {
    fn greet(&self) -> String {
        format!("Woof, I am {}", self.name)
    }
}

A trait declares methods types must implement. impl Greet for Dog provides the body. Rust uses structural matching, you must write the impl explicitly.

Why it works: traits let different types share a common interface.

Default methods

Built-in behavior

trait Animal {
    fn name(&self) -> &str;
    fn describe(&self) -> String {
        format!("This is {}", self.name())
    }
}

Traits can provide a default implementation. Types use it unless they override it, saving repeated code.

Trait bounds

Constraining generics

fn greet<T: Greet>(animal: &T) -> String {
    animal.greet()
}

: Greet is a trait bound. It says the generic type T must implement Greet, so greet can call .greet() on it.

Why it works: bounds let generic functions use shared behavior.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Structs and Methods
Previous Lesson
Structs and Methods
Next Lesson
Advanced Ownership
Advanced Ownership →