Traits and generics
Traits define shared behaviour, and generics let one function work across many types with bounds keeping them safe.
- 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
- 1Declare a trait with one or more method signatures.
- 2Implement the trait for a struct in an impl block.
- 3Write a generic function constrained by T: Summary.
- 4Rewrite the same bound with impl Trait and compare the two forms.
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.
Implement a Greet trait
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.
trait Greet {
fn greet(&self) -> String;
}
struct Name {
text: String,
}What does a trait bound such as T: Summary do?
Make it yours.
Keep a private note for this lesson, then turn the key ideas into flashcards you can revisit.
Lock in what you just learned.
Mark this lesson complete to save your place and move one step closer to the certificate.