Start learning free

LESSON 654 OF 679 · OBJECT-ORIENTED PROGRAMMING

OOP Capstone — Animal Hierarchy in Rust

Lesson

TL ; DR:

Put it all together: a trait, three concrete types that implement it, and a polymorphic loop that dispatches by runtime type. This is the shape of nearly every real OOP design.


trait Animal {
    fn name(&self) -> &str;
    fn sound(&self) -> &str;   // each implementor picks
    fn speak(&self) {           // shared by all
        println!("{}: {}", self.name(), self.sound());
    }
}
// Dog, Cat, Bird each implement Animal
// let zoo: Vec<Box<dyn Animal>> = vec![Box::new(Dog { name: "Rex".to_string() }), ...];
// for a in &zoo { a.speak(); }


Full lesson:

This problem stitches together everything you've learned in this chapter:

  1. Shared contract — Rust has no inheritance, so the Animal trait plays the role of the common parent.
  2. Structs — Dog, Cat and Bird each hold a name: String field.
  3. Required methods — the trait declares speak() with no body; each type must provide one.
  4. Per-type behavior — each impl Animal for block prints its own sound.
  5. Polymorphism — a Vec<Box<dyn Animal>> holds a Dog, a Cat, and a Bird; speak() dispatches to the right implementation at runtime.

The shape of this code is the shape of nearly every real OOP design you'll see — a small contract at the top (the trait), concrete variants below, and the rest of the program written against the trait. New variants get added without touching the consumer code.


Your task

The Animal trait is already defined, with a single method speak(&self). Add three structs, each with a name: String field and an impl Animal for block — Dog (prints "Woof"), Cat (prints "Meow"), and Bird (prints "Tweet") after the name. Then in main_function, build a polymorphic collection holding one of each (Rex, Whiskers, Tweety in that order) and loop through it calling speak().

Expected output:

Rex: Woof
Whiskers: Meow
Tweety: Tweet
STEP 1 OF 14
    Ctrl + Enter to check