Random Rust Notes - 2
Tags: rust
Published: 2023-04-17 16:48:28

---
title: Random Rust Notes - 2
published: true
description: A collection of notes I have made, while learning Rust
tags: rust
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6yjopg983ti1wtikg17r.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-04-17 16:42 +0000
---
[Cover image credit: https://pixabay.com/users/engin_akyurt-3656355/]

This blog is a collection of notes of key learnings / understandings / Aha moments, as I am kayaking through demanding yet exciting waters, of Rust (#rustlang). If these help someone, I will be happy of course. OTOH, if someone happens to find defects in the code or gaps in my understanding, please correct me. I will remain thankful.

As a part of a personal project, I am in the middle of learning how to use channels to communicate between two threads. This is quite a common topic. Many people seem to have questions around this topic. Many answers / explanations are available. 

These are my notes, though. 

### Takeaways from the previous note

The previous note is [here](https://dev.to/nsengupta/random-rust-notes-1-h51) (for a quick recap, in cases needed)

* A channel is created with two ends: one used for sending and other for receiving. While creating a channel, we specify the **type** of data that will pass through it, when the program runs.
* One thread gets ownership of the end that sends; the other thread gets ownership of the end that receives.
* The channel is always unidirectional, from the sending end to the receiving end. No Request-Response style inter-thread communication is possible, using a single channel.
* Any of the two ends may decide to stop working, **independent** of the other. No handshaking and *Good-Bye* is necessary. The thread that holds the sending end, may decide on its own, if and when to stop sending; similarly, for the thread that holds the receving end. 
* If one of the ends becomes non-operational, the other end receives an error. This  behaviour is captured by the return type of `send()` and `receive()` functions.

### The case of many senders (MPSC)

For those who remember the famous **Producer-Cconsumer ** problem from the textbooks (for me, a few decades ago :grin: ) - here's a [wikipedia](https://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem) page for a quick reference - the mechanism of channels should seem familiar. The thread that sends data into the channel, is the *producer* thread; the other thread which reads from the same channel, is the *consumer* thread. 

With only two threads, there is one producer and one consumner, commonly referred to as SPSC. What if there are multiple producers, a case of MPSC?

Obviously, each thread (a producer) has to have access to the sending end of the channel. Put even more simply, every thread has to have a sender for itself.

Referring to the code snippet in the previous note, we have:

```rust
    // ... continuing in current thread, let's call it one_thread
    let (sender, receiver) = channel::<Conversation>();

    let another_thread =  // another thread starts here ...
        thread::spawn(move || { 
         let a_message = Conversation::Hello;
         sender.send(a_message).unwrap();

         let another_message = Conversation::HowAreYou;
         sender.send(another_message).unwrap();

     }) // another_thread ends here

     // ... continue in one_thread

     let receipt_of_hello = receiver.recv().unwrap(); // message 'Hello' is here
     let receipt_of_how_are_you = receiver.recv.unwrap(); // message 'HowAreYou' is here
     // .. go on and do the rest
```

Because of the `move` , the ownership of `sender` is transferred to closure above. `another_thread` then runs the closure. This easy arrangement is not an option, when there is `yet_another_thread`. The ownership of `sender` cannot be transferred to the closure for `yet_another_thread`, as well. What do we do?

We simply duplicate the `sender` and pass one each to the closures. The above code takes the form:

```rust
    // ... continuing in current thread, let's call it one_thread
    let (sender, receiver) = channel::<Conversation>();

    let another_sender = sender.clone();

    let another_thread =  // another thread starts here ...
        thread::spawn(move || { 
         let a_message = Conversation::Hello;
         another_sender.send(a_message).unwrap();

         let another_message = Conversation::HowAreYou;
         sender.send(another_message).unwrap();

     }) // another_thread

    let yet_another_sender = sender.clone();

    let yet_another_thread =  // another thread starts here ...
        thread::spawn(move || { 
         let a_message = Conversation::Hello;
         yet_another_sender.send(a_message).unwrap();

         let another_message = Conversation::HowAreYou;
         sender.send(another_message).unwrap();

     }) // yet_another_thread

     // ... continue in one_thread

     let receipt_of_hello = receiver.recv().unwrap(); // message 'Hello' is here
     let receipt_of_how_are_you = receiver.recv.unwrap(); // message 'HowAreYou' is here
     // .. go on and do the rest
```

Obviously, such an arrangement is applicable to more than two producers also. We just need as many duplicates of `sender`. 

```rust

for i in 0..5 {  // '5' could have been, say '1000'!
    let duplicate_sender = sender.clone();

    // another thread starts here; we are 
        thread::spawn(move || { 
         let a_message = Conversation::Hello;
         duplicate_sender.send(a_message).unwrap();

         let another_message = Conversation::HowAreYou;
         duplicate_sender.send(another_message).unwrap();

     }) // another_thread

}
```

### Multiple producers done, what about single consumer?

The channel is unidirectional. Producers send messages in the direction of the Consumer. The consumer has to keep waiting for the messages to arrive, until the `recv()` returns an error indicating that all producers are done with their action (refer to the previous note). Almost intuitively, a consumer is a loop:

```rust
while let Ok(message_received) = receiver.recv() {
         println!("Message received: {:?}",message_received);
}
```
A Rust [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=b1355cc07240e0af05f20ddf9dbfc729) is here.

A crucial understanding is about the **order** in which the consumer receives the messages. The channel is FIFO for every Producer -> Consumer, but not for all Producers -> Consumer. If producer-1 sends 2 messages (say, m-1-1 and m-1-2) and producer-2 sends 2 messages (say, m-2-1- and m-2-2) to the same consumer, then the order in which the messages reach can be:
*   m-1-1, m-2-1-, m-2-2, m-1-2, or
*   m-1-1, m-1-2,  m-2-1, m-2-2 , but 
*   m-1-1 always reaches before m-1-2 and m-2-1 always reaches before m-2-2-

### Takeaways

*   Using channels, multiple producers can send messages to a single consumer.
*   Each such producer must have its own copy of the sender-end of the channel. `clone` the sender-end for this.
*   Channels are unidirectional and FIFO in structure; therefore, every producer's messages to the consumer reach in the same order in which they are despatched.
*   Multiple producers' messages may reach in any random order, though.