Day 4 · Concept 10
Traits
A trait is a set of methods a type can implement. Like Go interfaces, but with an
explicit impl Trait for Type block — making the relationship visible.
The coherence rule prevents two different "Display" implementations for the same type.
1 · Define + implement
trait Greet {
fn greet(&self) -> String;
// Default method — opt-in override
fn shout(&self) -> String {
format!("{}!!!", self.greet().to_uppercase())
}
}
struct English;
struct Klingon;
impl Greet for English {
fn greet(&self) -> String { "Hello".into() }
}
impl Greet for Klingon {
fn greet(&self) -> String { "nuqneH".into() }
// shout() inherited from default
}
fn main() {
println!("{}", English.shout());
println!("{}", Klingon.shout());
}2 · Trait bounds — generics with constraints
use std::fmt::Display;
fn announce<T: Greet + Display>(g: &T) {
println!("{} says: {}", g, g.greet());
}
trait Greet { fn greet(&self) -> String; }
struct User(&'static str);
impl Greet for User { fn greet(&self) -> String { format!("hi, I'm {}", self.0) } }
impl Display for User {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.0) }
}
fn main() {
announce(&User("Alice"));
}3 · The coherence rule (orphan rule)
You can implement your trait for any type, OR any trait for your type. But not someone else's trait for someone else's type. This prevents two crates from defining conflicting implementations of the same trait for the same type.
Workaround. Wrap the foreign type in a "newtype" — a tuple struct
around it. Now it's your type and you can implement whatever trait you want.
4 · The std traits you'll meet first
| Trait | For |
|---|---|
Debug | println!("{:?}", x) |
Display | println!("{}", x) |
Clone | x.clone() |
Copy | Assignment copies instead of moves |
PartialEq / Eq | == |
PartialOrd / Ord | <, .sort() |
Default | T::default() |
From<T> / Into<T> | Conversions |
Iterator | The for-loop machinery |
5 · Common mistakes
- Trying to implement a foreign trait for a foreign type. Wrap in a newtype.
- Forgetting
use Trait;. Methods from traits aren't in scope unless the trait is imported. - Method-name conflicts. If two traits both have a method called
name, disambiguate withTrait::name(&x).
Found this useful?