Rust Foundations·Ownership as a design tool

Traits and generics

Traits define shared behaviour, and generics let one function work across many types with bounds keeping them safe.

Intermediate 14 min read Lesson 4 of 300 Challenge included
What you will be able to do
  • Declare shared behaviour once in a trait with method signatures.
  • Implement the trait for each type in its own impl block.
  • Write generic functions with type parameters and add bounds to restrict them.
  • Use impl Trait for a shorter bound when the type is only used in one place.

In plain English

A trait is a declaration of shared behaviour that lists method signatures, like a contract any type can promise to follow. You implement a trait for a type with an impl block, and the compiler checks that every method in the trait really exists on that type. A generic function is written with a type parameter in angle brackets, like fn with a T, so one function body can serve many concrete types. A trait bound such as T: Summary restricts a generic parameter to types that implement the trait, and impl Trait is a shorter way to write that bound in an argument or return position.

Step by step

  1. 1Declare a trait with one or more method signatures.
  2. 2Implement the trait for a struct in an impl block.
  3. 3Write a generic function constrained by T: Summary.
  4. 4Rewrite the same bound with impl Trait and compare the two forms.
main.rsRust
trait Summary {
    fn summarize(&self) -> String;
}

struct Lesson {
    title: String,
    minutes: u32,
}

impl Summary for Lesson {
    fn summarize(&self) -> String {
        format!("{} ({} min)", self.title, self.minutes)
    }
}

fn print_summary<T: Summary>(item: &T) {
    println!("{}", item.summarize());
}

fn describe(item: &impl Summary) -> String {
    item.summarize()
}

fn main() {
    let lesson = Lesson {
        title: String::from("Traits and generics"),
        minutes: 14,
    };
    print_summary(&lesson);
    println!("{}", describe(&lesson));
}

Common mistakes

  • Declaring a method in a trait but not implementing every one of them for a type fails to compile.
  • Forgetting a bound on a generic means the body cannot call trait methods on the type parameter.
  • Assuming a trait can hold fields is wrong; traits describe behaviour, not stored data.
  • Using impl Trait where you need the concrete type again can be limiting, because the type is not named.
Your turn

Implement a Greet trait

Medium

Define a trait Greet with a method greet(&self) -> String, implement it for a struct Name, and write a function that accepts any Greet type.

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

struct Name {
    text: String,
}
Check your understanding before moving on.
Knowledge checkQuestion 1

What does a trait bound such as T: Summary do?

Select an answer to continue
Study tools

Make it yours.

Keep a private note for this lesson, then turn the key ideas into flashcards you can revisit.

Sign in to save notes and flashcards.Both are private to your account and stay with your progress.
Sign in
Finished reading?

Lock in what you just learned.

Mark this lesson complete to save your place and move one step closer to the certificate.

Next: Error handling patterns
Lesson 4 of 300 · 0% of course