Hone logo
Hone
Problems

Building a Simple Watch Channel in Rust

This challenge asks you to implement a basic "watch channel" in Rust. A watch channel allows one party to "watch" another party's data, receiving notifications whenever that data changes. This is a fundamental pattern in concurrent programming and is useful for building reactive systems, UI updates, and more.

Problem Description

You are to create a WatchChannel struct in Rust that allows a sender to update a value and receivers to be notified of those updates. The WatchChannel should manage a shared value and a list of receiver functions (closures). When the sender updates the value, all registered receivers should be called with the new value.

Key Requirements:

  • WatchChannel Struct: This struct should hold the shared value and a vector of receiver functions (closures).
  • send Method: A method on WatchChannel that takes a new value and updates the shared value. This method should also call all registered receivers with the new value.
  • subscribe Method: A method on WatchChannel that takes a receiver function (a closure that accepts the shared value type) and adds it to the list of receivers.
  • unsubscribe Method: A method on WatchChannel that takes a receiver function (the same closure used in subscribe) and removes it from the list of receivers.
  • Generic Type: The WatchChannel should be generic over the type of the shared value.

Expected Behavior:

  • When send is called, the shared value is updated, and all subscribed receivers are invoked with the new value.
  • subscribe adds a receiver to the list, ensuring it will be called on subsequent send calls.
  • unsubscribe removes a receiver, preventing it from being called on subsequent send calls.
  • The channel should handle multiple subscribers concurrently.

Edge Cases to Consider:

  • What happens if unsubscribe is called with a receiver that was never subscribed? (Should be safe, no error needed)
  • What happens if send is called after all receivers have unsubscribed? (Should update the value internally, but no receivers are called)
  • Consider thread safety if you want to extend this in the future. For this challenge, assume single-threaded usage.

Examples

Example 1:

Input:
```rust
let mut channel: WatchChannel<i32> = WatchChannel::new();
let mut subscriber1 = |val: i32| { println!("Subscriber 1: {}", val); };
let mut subscriber2 = |val: i32| { println!("Subscriber 2: {}", val); };

channel.subscribe(subscriber1);
channel.subscribe(subscriber2);

channel.send(10);

Output:

Subscriber 1: 10
Subscriber 2: 10

Explanation: The channel is initialized with an i32 value. Two subscribers are added. When send(10) is called, both subscribers are invoked with the value 10.

Example 2:

Input:
```rust
let mut channel: WatchChannel<String> = WatchChannel::new();
let mut subscriber1 = |val: String| { println!("Subscriber 1: {}", val); };
let mut subscriber2 = |val: String| { println!("Subscriber 2: {}", val); };

channel.subscribe(subscriber1);
channel.send("Hello".to_string());
channel.unsubscribe(subscriber2);
channel.send("World".to_string());

Output:

Subscriber 1: Hello
Subscriber 1: World

Explanation: A string channel is created. Subscriber 1 is added. The first send calls subscriber 1 with "Hello". Subscriber 2 is then unsubscribed. The second send only calls subscriber 1 with "World".

Example 3: (Edge Case)

Input:
```rust
let mut channel: WatchChannel<f64> = WatchChannel::new();
let subscriber = |val: f64| { println!("Subscriber: {}", val); };

channel.subscribe(subscriber);
channel.unsubscribe(subscriber);
channel.send(3.14);

Output:

// No output

Explanation: A subscriber is added and immediately unsubscribed. The send call updates the internal value, but no receivers are called.

Constraints

  • The shared value type T must implement the Clone trait. This is necessary for copying the value to the receivers.
  • The subscribe and unsubscribe methods must accept closures that take a value of type T and return nothing (Fn(&T)).
  • The code should be reasonably efficient. Avoid unnecessary allocations or copies.
  • The solution must compile and run without panics.

Notes

  • Consider using a Vec to store the receivers.
  • Closures are a powerful way to represent callbacks in Rust.
  • Think about how to handle the ownership of the shared value. It should be owned by the WatchChannel.
  • This is a simplified version of a pub/sub system. Real-world implementations are often more complex and involve thread safety, error handling, and more sophisticated filtering mechanisms.
  • Focus on correctness and clarity first. Performance optimizations can be considered later.
Loading editor...
rust