Random Rust Notes - 1
Tags: rust
Published: 2023-04-17 12:27:00

---
title: Random Rust Notes - 1
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/fieo0rdpxxt5daynrb4i.jpg
# Use a ratio of 100:42 for best results.
published_at: 2023-04-17 12:27 +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.

### Using channels to communicate between threads

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. Here goes:

* Channels provide mechanism for any two threads to communicate asynchrounously. It is unidirectional in structure and behaviour. Every channel has (at least) a Sender and a Receiver. The names indicate which end of the channel is occupied by which. 

* If we can set up a channel in such a way, that one thread gets hold of the sending end (in other words, is the Sender) and the other thread gets hold of the receiving end (in other words, is the Receiver), then they can converse.  

* Using `std::sync::mpsc` crate, the pattern to create a channel is:
        `let (sender, receiver) = channel::<Conversation>();`

  `Conversation` is an enum. The messages that are sent over the channel, are of type `Conversation`. Almost every example I have come across on the 'Net, uses `i32` or `&str`, but such an enum is contextually more meaningful (in my view :grin: ). Thus, the name.

* In keeping with the norms followed in tramission of signals, senders are named as *tx* and receivers, as *rx*. Many blogs/dicsussions use these. Doesn't matter, does it? *tx == sender* and *rx == receiver*!
* Two threads are going to converse. Therefore, one thread has to have access to the `sender` and the other thread, obviously, gets the `receiver`.
  
### Channels are unidirectional

* A channel is unidirectional: the messages *always* go from the sending side to the receiving side. In other words, the thread holding the `sender`, begins the conversation by sending a message; the thread holding the `receiver`, takes action on the receipt of the message.
* A key understanding is that the receiver does not respond to the message through the same channel. These channels are not bi-directional and hence, cannot be used for a Request-Response model.

### How do two threads make use of the channel?

*   Create the channel first. Now, we have the `sender` and the `receiver`.
*   The act of creation must happen on some thread. After all, we are executing a piece of code, on some existing thread!
```
    // ... 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
```

*   The **order** in which another_thread is sending the messages, is the **order** in which the receiver is receiving them. This is because the channel is **FIFO** in structure and behaviour. This is an important observation, because what if there are more than one senders? More about it later.

### How do threads know when to stop?

*   An ongoing conversation stops, when one of the participants stops. Therefore, if either the Sender stops or the Receiver stops, the other side must have to know. This is important to understand.
*   Significantly, neither of  `std::sync::mpsc::{Receiver,Sender}` provides any mechanism to *stop* sending or receiving. But, they are *dropped*, meaning they cease to exist, perhaps because the scope in which they are defined, comes to an end.
*   We note that the sending side (`one_thread` above) and the receiving side (`another_thread` above) are completely disconnected (naturally, one argues, because they are threads). Neither of the two is aware of the continued existence of the other side. Unless the channel through which they are communicating offers help!
*   To help, the channel specifies this: if a sender fails to send - in other words, the act of sending returns with an error - the sender comes to know that the receiver has become non-existent (no point sending any more). Similarly, if the sender goes out existence (viz. being *dropped* perhaps), the receiver is handed an error (nothing is going to arrive any further). Based on this, the threads can decide how to deal with a disconnection from one another.
*   Because the action of send/receive may return with an error, the retrun type of of these functions is a `Result`.
*   A call to send() returns a `Result<(),SendError>`. A call to receive() returns a `Result<Conversation,RecvError>` . Of course, that `Conversation` is my example type. It can be any valid type (generically speaking, `Result<T,RecvError>`).
*    This `Result<..>` explains the use of `unwrap()` in the code snippet, above.
  
### Takeaways

* 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 `recv()` functions.