Rust
Beginner
1 min read
Defining and Implementing Traits
Example
use std::fmt;
// Trait definition with a required method and a default method
trait Animal {
fn name(&self) -> &str;
fn sound(&self) -> &str;
// Default implementation — can be overridden
fn describe(&self) -> String {
format!("{} says '{}'", self.name(), self.sound())
}
}
struct Dog { name: String }
struct Cat { name: String }
impl Animal for Dog {
fn name(&self) -> &str { &self.name }
fn sound(&self) -> &str { "woof" }
// Uses default describe()
}
impl Animal for Cat {
fn name(&self) -> &str { &self.name }
fn sound(&self) -> &str { "meow" }
// Override the default
fn describe(&self) -> String {
format!("{} says '{}' and ignores you", self.name(), self.sound())
}
}
// Display is a standard-library trait
impl fmt::Display for Dog {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Dog({})", self.name)
}
}
fn print_animal(a: &dyn Animal) {
println!("{}", a.describe());
}
fn main() {
let dog = Dog { name: "Rex".to_string() };
let cat = Cat { name: "Whiskers".to_string() };
print_animal(&dog);
print_animal(&cat);
println!("{dog}"); // uses Display impl
// Trait objects in a Vec
let animals: Vec<Box<dyn Animal>> = vec![
Box::new(Dog { name: "Buddy".to_string() }),
Box::new(Cat { name: "Luna".to_string() }),
];
for a in &animals {
println!("{}", a.describe());
}
}