Default Trait Implementations
In this page:
trait Name {
fn required(&self);
fn provided(&self) {
// default body
}
}
impl Name for Type {
fn required(&self) {}
}
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
// Define a trait `Summary` describing shared behavior
trait Summary {
// Define the function `summarize` taking `&self` and returning a String
fn summarize(&self) -> String {
String::from("(no summary available)")
}
}
// Define a struct `Article` to group related fields together
struct Article;
// Implement the `Summary` trait for `Article`
impl Summary for Article {}
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `a`, set to `Article`
let a = Article;
// Print "{}" to the console, substituting in `a.summarize()` (with a trailing newline)
println!("{}", a.summarize());
}
Login to try C/C++/Java/PHP code in the editor
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
// Define a trait `Summary` describing shared behavior
trait Summary {
// Define the function `summarize` taking `&self` and returning a String
fn summarize(&self) -> String {
String::from("(no summary available)")
}
}
// Define a struct `Post` to group related fields together
struct Post {
title: String,
}
// Implement the `Summary` trait for `Post`
impl Summary for Post {
// Define the function `summarize` taking `&self` and returning a String
fn summarize(&self) -> String {
format!("Post: {}", self.title)
}
}
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `post`, set to `Post { title: String::from("Rust Traits") }`
let post = Post { title: String::from("Rust Traits") };
// Print "{}" to the console, substituting in `post.summarize()` (with a trailing newline)
println!("{}", post.summarize());
}
Login to try C/C++/Java/PHP code in the editor
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
// Define a trait `Greeting` describing shared behavior
trait Greeting {
// Define the function `name` taking `&self` and returning a `String;`
fn name(&self) -> String;
// Define the function `greet` taking `&self` and returning a String
fn greet(&self) -> String {
format!("Hello, {}!", self.name())
}
}
// Define a struct `User` to group related fields together
struct User {
username: String,
}
// Implement the `Greeting` trait for `User`
impl Greeting for User {
// Define the function `name` taking `&self` and returning a String
fn name(&self) -> String {
self.username.clone()
}
}
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `u`, set to `User { username: String::from("kai") }`
let u = User { username: String::from("kai") };
// Print "{}" to the console, substituting in `u.greet()` (with a trailing newline)
println!("{}", u.greet());
}
Login to try C/C++/Java/PHP code in the editor
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
// Define a trait `Vehicle` describing shared behavior
trait Vehicle {
// Define the function `wheels` taking `&self` and returning an unsigned integer
fn wheels(&self) -> u32 {
4
}
// Define the function `describe` taking `&self` and returning a String
fn describe(&self) -> String {
format!("A vehicle with {} wheels", self.wheels())
}
}
// Define a struct `Motorcycle` to group related fields together
struct Motorcycle;
// Implement the `Vehicle` trait for `Motorcycle`
impl Vehicle for Motorcycle {
// Define the function `wheels` taking `&self` and returning an unsigned integer
fn wheels(&self) -> u32 {
2
}
}
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `m`, set to `Motorcycle`
let m = Motorcycle;
// Print "{}" to the console, substituting in `m.describe()` (with a trailing newline)
println!("{}", m.describe());
}
Login to try C/C++/Java/PHP code in the editor
- Assuming every trait method must be re-implemented by every type -- default implementations let you skip that when the default fits.
- Forgetting a default method can still call other required (non-default) methods of the same trait, relying on each implementer to provide those.
- Overriding a default method unintentionally by accidentally redefining it with a different signature.
- 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: