Implementing a Oneshot Channel in Rust
Oneshot channels provide a simple mechanism for sending a single piece of data from one thread to another. They are useful for scenarios where you need to communicate a result or signal between threads without the complexity of a persistent channel. This challenge asks you to implement a basic oneshot channel in Rust, demonstrating your understanding of channels and thread communication.
Problem Description
You are tasked with implementing a OneshotChannel struct in Rust that allows for sending a single message from a sender to a receiver. The OneshotChannel should provide two methods: send and recv.
send(message: T): Takes a value of typeTand sends it to the receiver. After sending, the sender should be unusable (i.e., subsequent calls tosendshould panic).recv(): Blocks the current thread until a message is received from the sender. Upon receiving a message, it returns the message. After receiving, the receiver should be unusable (i.e., subsequent calls torecvshould panic).
The channel should be designed such that:
- Only one message can be sent and received.
- If the sender is dropped before sending a message,
recv()should return an error. - If the receiver is dropped before receiving a message, the sender should be able to send the message, but the message will be lost.
- If both sender and receiver are dropped, no error is raised.
Examples
Example 1:
Input:
Sender: `let (tx, rx) = OneshotChannel::<i32>::new();`
Sender Thread: `tx.send(10);`
Receiver Thread: `let value = rx.recv();`
Output: `value == 10`
Explanation: The sender thread sends the integer 10 to the receiver thread, which then receives and stores it.
Example 2:
Input:
Sender: `let (tx, rx) = OneshotChannel::<String>::new();`
Receiver: `let rx = rx;` (Receiver drops the receiver early)
Sender Thread: `tx.send("Hello".to_string());`
Output: Panic in Sender Thread
Explanation: Because the receiver was dropped before receiving the message, the sender attempts to send to a non-existent receiver, resulting in a panic.
Example 3: (Edge Case - Sender Drops First)
Input:
Sender: `let (tx, rx) = OneshotChannel::<bool>::new();`
Sender Thread: `drop(tx);`
Receiver Thread: `let received = rx.recv();`
Output: `Err(Error::Disconnected)` (or similar error indicating disconnection)
Explanation: The sender is dropped before sending any data. The receiver attempts to receive, but the channel is disconnected, resulting in an error.
Constraints
- The
OneshotChannelshould be generic over the message typeT. - The
sendmethod should panic if called more than once. - The
recvmethod should panic if called more than once. - The
recvmethod should block until a message is available or the channel is disconnected. - Error handling for disconnected channels should be implemented using
std::sync::mpsc::channel::RecvError.
Notes
Consider using std::sync::mpsc::channel as a foundation for your implementation, but you are not required to use it directly. Think about how to ensure that only one message can be sent and received. The core challenge lies in managing the state of the channel to prevent multiple sends or receives. You'll need to use synchronization primitives (like Mutex and Condvar) to safely manage access to the channel's state across threads. The drop implementation of the sender and receiver is crucial for proper error handling and preventing memory leaks.