10 / 20 · Day 4
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

rust src/main.rs
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

rust src/main.rs · accept anything Greet-able
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

TraitFor
Debugprintln!("{:?}", x)
Displayprintln!("{}", x)
Clonex.clone()
CopyAssignment copies instead of moves
PartialEq / Eq==
PartialOrd / Ord<, .sort()
DefaultT::default()
From<T> / Into<T>Conversions
IteratorThe 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 with Trait::name(&x).
Found this useful?