Hone logo
Hone
Problems

Reactive Streams with a Reactor Pattern in Rust

The Reactor pattern is a concurrency pattern that allows a single thread to handle multiple I/O events concurrently. This challenge asks you to implement a simplified version of this pattern in Rust, focusing on the core concepts of event registration, event dispatching, and data transformation. Building a reactor is useful for handling asynchronous operations efficiently without relying on multiple threads for each connection.

Problem Description

You are tasked with creating a basic reactor system in Rust. The reactor should be able to register handlers for specific event types. When an event occurs, the reactor should identify the appropriate handler and invoke it with the event data. The reactor should be single-threaded and handle events sequentially.

What needs to be achieved:

  • Define an Event enum to represent different event types (e.g., Read, Write, Error).
  • Create a Reactor struct that can register handlers for specific event types.
  • Implement a handle_event method on the Reactor that takes an Event as input, finds the registered handler for that event type, and calls the handler.
  • Handlers should be closures that accept the event type and associated data.

Key Requirements:

  • The reactor must support multiple handlers for the same event type. Handlers should be invoked in the order they were registered.
  • The reactor should not panic if no handler is registered for a given event type. Instead, it should log an error message (using eprintln!) and continue processing.
  • The Reactor should own the registered handlers, preventing them from being deallocated prematurely.

Expected Behavior:

The handle_event method should:

  1. Check if a handler exists for the given event type.
  2. If a handler exists, call it with the event.
  3. If no handler exists, print an error message to standard error.

Edge Cases to Consider:

  • What happens if the reactor receives an event of an unknown type?
  • What happens if multiple handlers are registered for the same event type? (Order of execution matters)
  • What happens if a handler panics? (The reactor should continue processing other events.)

Examples

Example 1:

Input:
Reactor initialized with:
- Read event handler: |event: Event, data: Vec<u8>| { println!("Read event: {:?}", data); }
- Write event handler: |event: Event, data: String| { println!("Write event: {}", data); }

Event: Read(vec![1, 2, 3])

Output:

Read event: [1, 2, 3]

Explanation: The reactor receives a Read event. It finds the registered Read handler and calls it with the data.

Example 2:

Input:
Reactor initialized with:
- Read event handler: |event: Event, data: Vec<u8>| { println!("Read event: {:?}", data); }
- Write event handler: |event: Event, data: String| { println!("Write event: {}", data); }

Event: Write("Hello".to_string())

Output:

Write event: Hello

Explanation: The reactor receives a Write event. It finds the registered Write handler and calls it with the data.

Example 3:

Input:
Reactor initialized with:
- Read event handler: |event: Event, data: Vec<u8>| { println!("Read event: {:?}", data); }

Event: Error("Something went wrong".to_string())

Output:

Error: No handler registered for Error event.

Explanation: The reactor receives an Error event, but no handler is registered for this event type. An error message is printed to standard error.

Constraints

  • The Event enum must have at least three variants: Read, Write, and Error.
  • Handlers must be closures that take an Event and associated data as input. The data type for each event variant should be appropriate for the event type (e.g., Vec<u8> for Read, String for Write).
  • The reactor should be able to handle at least 100 events without significant performance degradation (though optimization is not the primary focus).
  • The solution must be written in idiomatic Rust.

Notes

  • Consider using a HashMap to store the event handlers.
  • Think about how to handle ownership of the handlers to prevent dangling closures. Using Box<dyn Fn(...)> is a common approach.
  • Error handling is important. The reactor should gracefully handle cases where no handler is registered for an event.
  • This is a simplified reactor. Real-world reactors are significantly more complex and often involve asynchronous I/O and thread pools. Focus on the core concepts of event registration and dispatching.
  • The data associated with each event is intentionally generic. You can adjust the types as needed to fit your implementation.
Loading editor...
rust