Default Methods
Trait defaults.
Default Methods is a free Learn Rust Coding lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Learn Rust Coding learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Methods With a Body
Trait methods do not have to be just signatures. A trait can provide a default implementation — a method body that implementors get automatically.
This reduces boilerplate across many types.
A Default Method
Write the body right inside the trait. Types that implement the trait inherit it for free, with no extra code.
trait Greet {
fn hello(&self) -> String {
String::from("Hello there")
}
}
struct Robot;
impl Greet for Robot {}
fn main() {
println!("{}", Robot.hello());
}Overriding a Default
Any implementor may override a default by providing its own version. The override replaces the default for that type.
trait Greet {
fn hello(&self) -> String { String::from("Hello there") }
}
struct Robot;
struct Pirate;
impl Greet for Robot {}
impl Greet for Pirate {
fn hello(&self) -> String { String::from("Arr!") }
}
fn main() {
println!("{}", Robot.hello());
println!("{}", Pirate.hello());
}Defaults Calling Required Methods
A default method can call other methods of the same trait, even ones with no default. This is a powerful pattern: implementors supply a small core, and defaults build richer behavior on top.
trait Summary {
fn title(&self) -> String;
fn preview(&self) -> String {
format!("Read more about: {}", self.title())
}
}
struct Article { headline: String }
impl Summary for Article {
fn title(&self) -> String { self.headline.clone() }
}
fn main() {
let a = Article { headline: String::from("Rust Rocks") };
println!("{}", a.preview());
}The Template Method Pattern
This required-plus-default combination is the template method pattern. The trait defines the overall flow in defaults and leaves the variable parts as required methods.
trait Report {
fn body(&self) -> String;
fn render(&self) -> String {
format!("=== REPORT ===\n{}\n==============", self.body())
}
}
struct Sales;
impl Report for Sales {
fn body(&self) -> String { String::from("Sales up 10%") }
}
fn main() {
println!("{}", Sales.render());
}Multiple Defaults
A trait can mix required and default methods freely. Implementors only need to supply the required ones.
trait Animal {
fn name(&self) -> String;
fn legs(&self) -> u32 { 4 }
fn describe(&self) -> String {
format!("{} has {} legs", self.name(), self.legs())
}
}
struct Dog;
impl Animal for Dog {
fn name(&self) -> String { String::from("Dog") }
}
fn main() {
println!("{}", Dog.describe());
}Defaults and Standard Traits
Many standard traits rely on defaults. For instance, Iterator requires only next; every adapter and consumer is a default method built on it.
That is why one method unlocks the whole toolbox.
Overriding for Performance
Sometimes a default works but a specialized version is faster. Implementors can override just that method while keeping the rest of the defaults.
trait Counter {
fn items(&self) -> Vec<i32>;
fn total(&self) -> i32 {
self.items().iter().sum()
}
}
struct Fast { precomputed: i32 }
impl Counter for Fast {
fn items(&self) -> Vec<i32> { vec![] }
fn total(&self) -> i32 { self.precomputed }
}
fn main() {
println!("{}", Fast { precomputed: 99 }.total());
}Keeping Traits Ergonomic
Good trait design: make a small set of methods required, and provide convenient defaults for the rest. Implementors do minimal work but get a rich interface.
A Caveat
A default method can only use other trait methods and the type behind self through them; it cannot access fields of an unknown implementor directly. Design defaults around the trait's own methods.
Defaults vs Required at a Glance
A trait method with a body is a default; one with only a signature is required. Implementors must supply every required method but may leave defaults untouched.
trait Logger {
fn line(&self) -> String;
fn log(&self) {
println!("LOG: {}", self.line());
}
}
struct App;
impl Logger for App {
fn line(&self) -> String { String::from("started") }
}
fn main() {
App.log();
}Quick Check
Test your understanding of default methods.
Recap
You learned trait defaults:
- A trait method can include a body as a default implementation
- Implementors inherit it for free and may override it
- Defaults can call required methods, enabling the template-method pattern
- This keeps traits ergonomic with a small required surface
Frequently asked questions
Is the “Default Methods” lesson free?
Yes — the full text of “Default Methods” is free to read here on the web, and the Learn Rust Coding course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Learn Rust Coding course, upgrade to CoddyKit PRO.
What will I learn in “Default Methods”?
Trait defaults. You practise Learn Rust Coding with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Learn Rust Coding?
No prior experience is required. Learn Rust Coding on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Default Methods” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Learn Rust Coding lesson?
Yes. Every Learn Rust Coding lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.