Traits define shared behavior that types can implement, similar to interfaces in other languages. They enable polymorphism and code reuse without inheritance.
trait Summary {
// Required method (no default implementation)
fn summarize(&self) -> String;
// Default implementation
fn describe(&self) -> String {
format!("This is a summary: {}", self.summarize())
}
}struct Article {
headline: String,
content: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
format!("{} - {}", self.headline, self.content)
}
}Restrict generic types to those implementing a trait.
fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}fn notify<T: Summary + Display>(item: &T) { ... }fn notify<T>(item: &T)
where
T: Summary + Display,
{ ... }Traits can provide default method implementations:
trait Summary {
fn summarize(&self) -> String {
String::from("(Read more...)")
}
}trait DisplaySummary: Summary {
fn display(&self);
}Common traits Rust can auto-implement:
Debug,Clone,Copy,PartialEq,Eq,PartialOrd,Ord,Hash
#[derive(Debug, Clone)]
struct Point {
x: i32,
y: i32,
}Enable dynamic dispatch with dyn Trait:
fn dynamic_notify(item: &dyn Summary) {
println!("{}", item.summarize());
}- Use Case: When you need heterogeneous collections.
- Trade-off: Slight runtime overhead (vs. static dispatch).
Define placeholder types in traits:
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}impl<T: Display> Summary for T {
fn summarize(&self) -> String {
format!("Displayable value: {}", self)
}
}| Trait | Purpose |
|---|---|
Debug |
Enables formatting for debugging ({:?}). |
Display |
Enables user-friendly formatting ({}). |
Clone |
Enables explicit duplication with .clone(). |
PartialEq |
Enables equality comparisons (==, !=). |
Iterator |
Defines iterator behavior (next(), map(), etc.). |
From/Into |
Enables type conversions. |
Default |
Provides a default value (T::default()). |
- Required Methods: Must be implemented by types.
- Default Methods: Optional to override.
trait Greet {
fn greet(&self) -> String;
}
impl Greet for String {
fn greet(&self) -> String {
format!("Hello, {}!", self)
}
}
fn say_hello<T: Greet>(item: T) {
println!("{}", item.greet());
}- Polymorphism: Write generic code that works with multiple types.
- Code Reuse: Share behavior across unrelated types.
- Abstraction: Define interfaces without inheritance.
- Zero-Cost Abstraction: Static dispatch (generics) has no runtime overhead.
- Use traits for shared behavior, not data.
- Prefer static dispatch (generics) over dynamic dispatch (
dyn) for performance. - Use default implementations to reduce boilerplate.
- Document trait purposes and invariants with
///comments.