iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Traits

Traits define shared behaviour — like interfaces. Types implement them with impl Trait for Type. Generics + traits give you C++-like zero-cost abstractions with compile-time dispatch.

Define, implement, derive, blanket impls

EXAMPLE
// 1) Define a trait
trait Greet {
    fn hello(&self) -> String;                         // required
    fn shout(&self) -> String {                         // default method
        self.hello().to_uppercase() + "!"
    }
}

// 2) Implement
struct User { name: String }

impl Greet for User {
    fn hello(&self) -> String {
        format!("Hi, I'm {}", self.name)
    }
}

fn main() {
    let u = User { name: "Ada".into() };
    println!("{}", u.hello());
    println!("{}", u.shout());
}

// 3) Derive — common traits get auto-implementations
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct Tag(String);

let t = Tag("foo".into());
println!("{:?}", t);          // Debug
let copy = t.clone();          // Clone
assert_eq!(t, copy);           // PartialEq

// 4) Trait bounds on generics — works for any T that implements the trait
fn print_greeting<T: Greet>(thing: T) {
    println!("{}", thing.hello());
}

// or impl Trait syntax
fn make_greeting(thing: impl Greet) -> String {
    thing.hello()
}

// 5) Multiple bounds
fn show<T: std::fmt::Debug + Clone>(x: T) {
    println!("{:?}", x.clone());
}

// 6) where clause — cleaner for long bounds
fn process<T, U>(t: T, u: U) -> String
where
    T: std::fmt::Debug,
    U: Greet + Clone,
{
    format!("{:?} / {}", t, u.clone().hello())
}

// 7) Trait objects — dynamic dispatch
fn print_all(items: &[Box<dyn Greet>]) {
    for item in items {
        println!("{}", item.hello());
    }
}

// 8) Blanket implementations — implement a trait for ALL types that meet a bound
trait JsonString {
    fn to_json_string(&self) -> String;
}

impl<T: serde::Serialize> JsonString for T {
    fn to_json_string(&self) -> String {
        serde_json::to_string(self).unwrap()
    }
}

// 9) Associated types
trait Iterator2 {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
}

// 10) Common standard-library traits to know
//   Display       : fmt::Display, the user-facing string
//   Debug         : fmt::Debug, the developer-facing string
//   Clone, Copy   : value duplication
//   PartialEq, Eq : equality
//   Hash          : hashable for HashMap / HashSet
//   Default       : default value
//   From<T>, Into<T> : conversions
//   Iterator      : the iterator protocol
//   Drop          : run code when value is dropped

// 11) Sealed traits — restrict who can implement (use a private supertrait)
mod sealed { pub trait Sealed {} }
pub trait MyTrait: sealed::Sealed { /* ... */ }
// Only types in your crate can `impl sealed::Sealed`, so only your crate can implement MyTrait.

Why it matters

Traits are how you express “any type with these capabilities” in Rust. Combined with generics, the compiler monomorphises into specific code at zero runtime cost — abstraction without overhead.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
trait Greet {
    fn hello(&self) -> String;
}

impl Greet for User {
    fn hello(&self) -> String { format!("hi, {}", self.name) }
}
Try it Yourself »

Exercise

Implement a trait for a type.

Greet for User { fn hello(&self) -> String { "hi".into() } }

Discussion

Loading…