← Back to Rust Course | Chapter 11: Generics & Traits | Lesson 5 of 7

Default Trait Implementations

A default implementation is a ready-made behavior a trait provides for free, which types can use as-is or replace with their own.

Defining a Default Method

A trait method can include a body directly in the trait definition, and any type implementing the trait automatically gets that behavior unless it overrides it.

Example: Defining a Default Method

markup
trait Summary {
    fn summarize(&self) -> String {
        String::from("(no summary available)")
    }
}

struct Article;

impl Summary for Article {}

fn main() {
    let a = Article;
    println!("{}", a.summarize());
}

Overriding a Default Method

A type implementing the trait can still provide its own version of a method that has a default, replacing the default behavior entirely for that type.

Example: Overriding a Default Method

markup
trait Summary {
    fn summarize(&self) -> String {
        String::from("(no summary available)")
    }
}

struct Post {
    title: String,
}

impl Summary for Post {
    fn summarize(&self) -> String {
        format!("Post: {}", self.title)
    }
}

fn main() {
    let post = Post { title: String::from("Rust Traits") };
    println!("{}", post.summarize());
}

Default Methods Calling Required Methods

A default method can call another method of the same trait that has no default, relying on every implementing type to provide that required piece while reusing shared logic around it.

Example: Default Methods Calling Required Methods

markup
trait Greeting {
    fn name(&self) -> String;
    fn greet(&self) -> String {
        format!("Hello, {}!", self.name())
    }
}

struct User {
    username: String,
}

impl Greeting for User {
    fn name(&self) -> String {
        self.username.clone()
    }
}

fn main() {
    let u = User { username: String::from("kai") };
    println!("{}", u.greet());
}

Mixing Default and Overridden Methods

A trait can have several methods, some with defaults and some overridden by a given type, letting each implementer reuse convenient shared logic while customizing only what it needs.

Example: Mixing Default and Overridden Methods

markup
trait Vehicle {
    fn wheels(&self) -> u32 {
        4
    }
    fn describe(&self) -> String {
        format!("A vehicle with {} wheels", self.wheels())
    }
}

struct Motorcycle;

impl Vehicle for Motorcycle {
    fn wheels(&self) -> u32 {
        2
    }
}

fn main() {
    let m = Motorcycle;
    println!("{}", m.describe());
}
Common Mistakes
  1. Assuming every trait method must be re-implemented by every type -- default implementations let you skip that when the default fits.
  2. Forgetting a default method can still call other required (non-default) methods of the same trait, relying on each implementer to provide those.
  3. Overriding a default method unintentionally by accidentally redefining it with a different signature.
Chapter Summary
  • A trait method can provide a default body, which implementing types automatically inherit unless they override it.
  • Types can still choose to override a default implementation with their own custom version.
  • Default methods can call other methods of the same trait, even ones without a default, relying on each type to supply those.
  • Default implementations reduce repetitive boilerplate across many types implementing the same trait.
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.