w3resource

Implementing Request-Response Pattern with Channels in Rust

Rust Error Propagation: Exercise-9 with Solution

Write a Rust program to implement a request-response pattern using channels. Create one thread to handle requests and another thread to handle responses. When a request is received, the handler thread processes it and sends a response back through the channel.

Sample Solution:

Rust Code:

use std::sync::{mpsc, Arc, Mutex};
use std::thread;

fn main() {
    // Create channels for communication between request and response handlers
    let (request_sender, request_receiver) = mpsc::channel(); // Create a channel for sending requests
    let (response_sender, response_receiver) = mpsc::channel(); // Create a channel for sending responses
    let response_receiver = Arc::new(Mutex::new(response_receiver)); // Wrap the response receiver in an Arc and a Mutex for safe sharing among threads

    // Spawn a thread to handle requests
    let request_handler = thread::spawn(move || { // Spawn a new thread
        for request in request_receiver { // Iterate over received requests
            // Process the request (e.g., simulate processing time)
            let processed_request = format!("Processed: {}", request);
            // Send the response back through the channel
            response_sender.send(processed_request).unwrap();
        }
    });

    // Spawn a thread to handle responses
    let cloned_response_receiver = Arc::clone(&response_receiver); // Clone the Arc to share ownership with the response handler thread
    let response_handler = thread::spawn(move || { // Spawn a new thread
        // Get the inner receiver from the Arc
        let receiver = cloned_response_receiver.lock().unwrap(); // Lock the mutex to access the receiver
        for response in receiver.iter() { // Iterate over received responses
            // Handle the response (e.g., print it)
            println!("Received response: {}", response);
        }
    });

    // Simulate sending requests
    for i in 0..5 {
        // Send a request through the channel
        request_sender.send(i.to_string()).unwrap();
        // Receive the response
        let response = response_receiver.lock().unwrap().recv().unwrap();
        // Print the received response
        println!("Received response to request {}: {}", i, response);
    }

    // Wait for request and response handler threads to finish
    request_handler.join().unwrap();
    response_handler.join().unwrap();
}

Output:

Received response to request 0: Processed: 0
Received response to request 1: Processed: 1
Received response to request 2: Processed: 2
Received response to request 3: Processed: 3
Received response to request 4: Processed: 4

Explanation:

In the exercise above,

  • Two channels are created: 'request_sender' and 'request_receiver' for handling requests, and 'response_sender' and 'response_receiver' for handling responses. The 'response_receiver' is wrapped in an 'Arc' and a 'Mutex' for safe sharing among threads.
  • A thread is spawned to handle requests ('request_handler'). It listens for requests from the 'request_receiver', processes them (e.g., simulates processing time), and sends back the responses through the 'response_sender'.
  • Another thread is spawned to handle responses ('response_handler'). It locks the 'response_receiver' mutex, iterates over received responses, and handles them (e.g., prints them).
  • The main thread simulates sending requests by iterating over a range of values. For each value, it sends a request through the 'request_sender', waits for the response by receiving it from the 'response_receiver', and prints the received response.
  • Finally, the main thread waits for both the request and response handler threads to finish using "join()".

Rust Code Editor:

Previous: Demonstrating Buffered Channels in Rust.

What is the difficulty level of this exercise?

Test your Programming skills with w3resource's quiz.



Follow us on Facebook and Twitter for latest update.