Overriding Defaults
Part of the Object Oriented Programming section of Coddy's Rust journey — lesson 32 of 61.
Default implementations are convenient, but sometimes a type needs behavior that differs from the standard. When this happens, you can override the default by providing your own implementation in the impl block.
Simply define the method with the same signature, but include your custom logic:
trait Greet {
fn greet(&self) -> String {
String::from("Hello there!")
}
}
struct Robot {
id: u32,
}
impl Greet for Robot {
fn greet(&self) -> String {
format!("BEEP BOOP. Unit {} online.", self.id)
}
}
Even though Greet provides a default greet method, Robot replaces it entirely with its own version. When you call greet() on a Robot, Rust uses the custom implementation:
let bot = Robot { id: 42 };
println!("{}", bot.greet()); // BEEP BOOP. Unit 42 online.
This gives you flexibility—types that are happy with the default don't need to write anything, while types that need specialized behavior can override just the methods they care about. The trait still guarantees that every implementing type has the method available.
Challenge
EasyLet's build a notification system where different alert types can customize their messages! You'll create a trait with a default implementation, then have one type use the default while another overrides it with custom behavior.
You'll organize your code across three files:
notifiable.rs: Define a publicNotifiabletrait with anotify(&self) -> Stringmethod that has a default implementation returning"Alert: Something happened!".alerts.rs: Create two public structs that implement your trait differently:GenericAlert— a unit struct that uses the default notification (empty impl block)UrgentAlert— a struct with a publicmessagefield (String) that overrides the default to return"URGENT: {message}"where{message}is its stored message
main.rs: Bring your modules together and demonstrate both behaviors. Create aGenericAlertand anUrgentAlertusing the input provided, then print their notifications.
The key insight here is that GenericAlert gets free behavior from the default, while UrgentAlert provides its own specialized version by defining the method in its impl block.
Your output should follow this format:
Alert: Something happened!
URGENT: {message}For example, with input Server is down!:
Alert: Something happened!
URGENT: Server is down!You will receive one input: the message for the urgent alert.
Cheat sheet
You can override default trait implementations by providing your own implementation in the impl block:
trait Greet {
fn greet(&self) -> String {
String::from("Hello there!")
}
}
struct Robot {
id: u32,
}
impl Greet for Robot {
fn greet(&self) -> String {
format!("BEEP BOOP. Unit {} online.", self.id)
}
}
let bot = Robot { id: 42 };
println!("{}", bot.greet()); // BEEP BOOP. Unit 42 online.
Types that don't need custom behavior can use an empty impl block to accept the default implementation, while types requiring specialized behavior can override specific methods by defining them with the same signature.
Try it yourself
mod notifiable;
mod alerts;
use alerts::{GenericAlert, UrgentAlert};
use notifiable::Notifiable;
fn main() {
// Read input for the urgent alert message
let mut input = String::new();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let message = input.trim().to_string();
// TODO: Create a GenericAlert instance
// TODO: Create an UrgentAlert instance with the input message
// TODO: Print the notification from GenericAlert
// TODO: Print the notification from UrgentAlert
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Methods and Behavior
Intro Implementation BlocksThe Self ParameterMutable MethodsAssociated FunctionsMultiple Implementation BlocksMethod ChainingRecap - Rectangle Actions4Project: Virtual Pet
Defining the PetFeeding the Pet7Standard Traits
The Debug TraitThe Display TraitClone and CopyEquality TraitsRecap - Printable Point10Project: Document System
The Draw TraitText Component2Encapsulation and Modules
Modules BasicsThe Public KeywordPrivate FieldsGettersSettersRecap - Secure Locker5Generics
Generic StructsGeneric MethodsMultiple Generic TypesGeneric FunctionsRecap - Coordinate Point8Traits as Bounds
Trait Bounds SyntaxMultiple BoundsThe Where ClauseReturning Types with TraitsRecap - Generic Printer11Design Patterns in Rust
Newtype PatternCompositionThe Drop TraitFrom and IntoRecap - Smart Pointer Mock3Advanced Enums
Enums with DataMethods on EnumsMatching Data VariantsThe Option Enum RevisitedRecap - Shape Enum6Traits Definition
What is a Trait?Implementing TraitsDefault ImplementationsOverriding DefaultsTraits with ParametersRecap - Media Player