Hone logo
Hone
Problems

Rust Dynamic Dispatch: Implementing a Generic Behavior System

This challenge will guide you through implementing dynamic dispatch in Rust using trait objects. You will create a system where different types can share a common interface, allowing for flexibility and extensibility in your code without resorting to monomorphization for every single type. This is crucial for scenarios like plugin systems, collections of heterogeneous objects, or when the exact type of an object is not known at compile time.

Problem Description

Your task is to design and implement a system that allows for dynamic dispatch of behavior to different types. You will define a trait that represents a common behavior, and then implement this trait for several distinct types. Finally, you will demonstrate how to store and invoke this behavior on a collection of these types using trait objects (dyn Trait).

Key Requirements:

  1. Define a Trait: Create a trait named Greeter with a single method, greet(&self) -> String. This method should return a personalized greeting string.
  2. Implement the Trait:
    • Implement Greeter for a struct Person that has a name: String field. The greeting should be "Hello, my name is [name]!".
    • Implement Greeter for a struct Robot that has an id: u32 field. The greeting should be "Beep boop, I am Robot #[id]!".
    • Implement Greeter for a struct Alien that has a species: String field. The greeting should be "Greetings, traveller from the [species] sector!".
  3. Use Dynamic Dispatch: Create a Vec that can hold instances of Person, Robot, and Alien polymorphically. This Vec should store them as trait objects.
  4. Invoke Behavior: Iterate over the Vec of trait objects and call the greet method on each one, printing the returned string to the console.

Expected Behavior:

When the program runs, it should print a sequence of greetings, each corresponding to an instance of Person, Robot, or Alien stored in the collection. The order of greetings should match the order of insertion into the collection.

Edge Cases:

  • Empty collection: The program should handle an empty collection gracefully (i.e., not panic and simply print nothing).

Examples

Example 1:

// Assume the Person, Robot, and Alien structs are defined and Greeter trait is implemented.
// We create a collection and add different types.

let mut greetables: Vec<Box<dyn Greeter>> = Vec::new();
greetables.push(Box::new(Person { name: "Alice".to_string() }));
greetables.push(Box::new(Robot { id: 123 }));

// When iterating and calling greet:
// Output should be:
// Hello, my name is Alice!
// Beep boop, I am Robot #123!

// Explanation:
// The Person instance "Alice" is greeted, followed by the Robot instance with ID 123.

Example 2:

// Assume the Alien struct is also defined and Greeter trait is implemented.
// Adding more types to the collection.

let mut greetables: Vec<Box<dyn Greeter>> = Vec::new();
greetables.push(Box::new(Alien { species: "Zorgon".to_string() }));
greetables.push(Box::new(Person { name: "Bob".to_string() }));
greetables.push(Box::new(Robot { id: 456 }));

// When iterating and calling greet:
// Output should be:
// Greetings, traveller from the Zorgon sector!
// Hello, my name is Bob!
// Beep boop, I am Robot #456!

// Explanation:
// The Alien, Person, and Robot instances are greeted in the order they were added.

Example 3: Empty Collection

// An empty collection.
let greetables: Vec<Box<dyn Greeter>> = Vec::new();

// When iterating and calling greet:
// Output should be:
// (nothing printed)

// Explanation:
// The loop does not execute for an empty vector, so no greetings are printed.

Constraints

  • The Greeter trait must have a method greet(&self) -> String.
  • The Person, Robot, and Alien structs must be defined as specified.
  • The collection must be a Vec storing boxed trait objects (Box<dyn Greeter>).
  • No external crates should be used for the core dynamic dispatch mechanism.

Notes

  • Consider how you will store different types that implement the same trait in a single collection. Box<dyn Trait> is a common way to achieve this with heap allocation.
  • Remember that dyn Trait requires the trait to be "object-safe." The Greeter trait as defined is object-safe.
  • Think about how the compiler handles dynamic dispatch versus static dispatch (monomorphization). This exercise focuses on the former.
Loading editor...
rust