Hone logo
Hone
Problems

Asynchronous Producer-Consumer with Channels

Asynchronous programming in Rust often involves communication between different asynchronous tasks. Channels are a fundamental primitive for this, allowing one task to send data to another without blocking. This challenge will test your understanding of creating and using asynchronous channels to implement a classic producer-consumer pattern.

Problem Description

You are tasked with building a system that simulates a producer sending data to a consumer asynchronously. The producer will generate a sequence of numbers, and the consumer will receive and process them. The communication between the producer and consumer must happen via an asynchronous channel.

Requirements:

  1. Create an Asynchronous Channel: Use Rust's tokio::sync::mpsc (multi-producer, single-consumer) channel to facilitate communication.
  2. Implement a Producer Task:
    • This task should generate a specified number of integers (e.g., from 0 up to N-1).
    • Each generated integer should be sent through the channel.
    • After sending all integers, the producer should signal that it's done by closing the sender.
  3. Implement a Consumer Task:
    • This task should continuously receive messages from the channel.
    • For each received message, it should print the received integer.
    • The consumer should gracefully exit when the channel is closed and no more messages are expected.
  4. Orchestrate Tasks: Use tokio::spawn to run the producer and consumer tasks concurrently. Ensure the main function waits for both tasks to complete.

Expected Behavior:

The producer should send numbers, and the consumer should receive and print them in the order they were sent. The program should terminate cleanly once all numbers have been produced and consumed.

Edge Cases:

  • Zero Items: What happens if the producer is asked to produce zero items?
  • Large Number of Items: How does the system perform with a very large number of items? (While not strictly enforced by constraints here, consider the implications).

Examples

Example 1:

Input:
Number of items to produce: 5

Output:
Producer started.
Producing: 0
Producing: 1
Producing: 2
Producing: 3
Producing: 4
Producer finished.
Consumer started.
Received: 0
Received: 1
Received: 2
Received: 3
Received: 4
Consumer finished.

Explanation: The producer sends numbers 0 through 4. The consumer receives and prints each number. After the producer sends 4, it closes the sender. The consumer then receives the "end of stream" signal and also terminates.

Example 2:

Input:
Number of items to produce: 0

Output:
Producer started.
Producer finished.
Consumer started.
Consumer finished.

Explanation: The producer is asked to produce zero items. It immediately finishes and closes the sender. The consumer starts, sees the channel is closed, and terminates without receiving any data.

Constraints

  • The number of items to produce will be a non-negative integer.
  • The solution must use the tokio runtime and its mpsc channel.
  • The solution should be implemented entirely in Rust.

Notes

  • You'll need to add tokio as a dependency in your Cargo.toml.
  • Remember that tokio::sync::mpsc::channel returns a Sender and a Receiver.
  • The Sender can be cloned to allow multiple producers if needed (though this challenge focuses on one).
  • The Receiver needs to be iterated over (using while let Some(value) = rx.recv().await) to receive messages until the channel is closed.
  • Consider how to gracefully handle the receiver end when the sender is dropped.
Loading editor...
rust