Rust Traits
A trait defines a set of behavior that a type can implement — a bit like an interface in other languages. It’s how Rust describes “this type can do X,” and it’s what powers the trait bounds you saw on the Generics page.
Defining a trait
Section titled “Defining a trait”trait Speak { fn speak(&self) -> String;}This says: any type that implements Speak must provide a speak method that returns a String. On its own, a trait is just a promise — it doesn’t do anything until some type implements it.
Implementing a trait
Section titled “Implementing a trait”trait Speak { fn speak(&self) -> String;}
struct Dog;struct Cat;
impl Speak for Dog { fn speak(&self) -> String { String::from("Woof!") }}
impl Speak for Cat { fn speak(&self) -> String { String::from("Meow!") }}
fn main() { let dog = Dog; let cat = Cat;
println!("{}", dog.speak()); println!("{}", cat.speak());}Output:
Woof!Meow!Dog and Cat are unrelated types, but both implement Speak, so both can be called with .speak(). This is the same pattern as impl blocks on the Structs page — impl Speak for Dog just says “here’s how Dog fulfills the Speak trait.”
Traits as function parameters
Section titled “Traits as function parameters”The real payoff: you can write a function that accepts any type implementing a trait, without caring which specific type it is:
trait Speak { fn speak(&self) -> String;}
struct Dog;impl Speak for Dog { fn speak(&self) -> String { String::from("Woof!") }}
fn announce(animal: &impl Speak) { println!("The animal says: {}", animal.speak());}
fn main() { let dog = Dog; announce(&dog);}animal: &impl Speak means “any type that implements Speak, I don’t need to know which one.” Add a Bird type that also implements Speak, and announce works with it too, with no changes.
Traits you’ve already used
Section titled “Traits you’ve already used”Remember #[derive(Debug)] from the Structs page? Debug is itself a trait — derive just auto-generates the implementation for you instead of you writing it by hand. Other common ones you’ll run into: Clone (for .clone()), PartialEq (for ==), and PartialOrd (for <, >, which you saw used as a trait bound on the Generics page).
Default implementations
Section titled “Default implementations”A trait can provide a default method body, which implementing types can use as-is or override:
trait Greet { fn name(&self) -> String;
fn greet(&self) -> String { format!("Hello, {}!", self.name()) }}Any type implementing Greet only has to define name() — greet() comes for free, unless it chooses to override it too.
- A trait defines behavior a type can implement, similar to an interface.
impl TraitName for Typeprovides the implementation.&impl TraitNameas a parameter type accepts any type implementing that trait.- Traits can supply default method bodies that implementers can use or override.
Debug,Clone, andPartialEqare common traits from the standard library —#[derive(...)]implements them automatically.