Channels in Golang
Tags: go
Published: 2023-01-16 21:29:50

“Channels are a typed conduit through which you can send and receive values with the channel operator, `<-`“. This is the definition of channels in the Golang [documentation](https://go.dev/tour/concurrency/2) but what exactly does that mean?

First, you can look at channels as some form of portal between two worlds, where you throw something through it from world A and it appears in world B.

Let’s take an example;

Imagine, we’re in a multiverse. We’re in WorldA and trying to send message to our other self in WorldB that our nemesis is coming.

We first create a channel using the make function. This will take `chan` as an argument to specify that it is a channel and the type will be the type of data we want to pass around. So we’ll create a channel that takes a string, `“Our nemesis is coming”` and pass the data through the portal. To pass a data through a portal, you use the expression: `<-`. The expression is like an arrow and the arrow direction is the direction of the data flow.

```go
package main

import "fmt"

func main(){
	// Create our channel which we are naming portal
	portal := make(chan string)
	
	// This is the message we're sending to our other self in WorldB
	msg := "Our nemesis is coming" 
	
	// Message goes through portal
	portal <- msg

	// Message is received by our other self in World B
	worldBSelf := <- portal
	
	// We're going to print our message to see if everything is working well.
	fmt.Println(worldBSelf)
}
```

Now when we run our code, we realise we have an error like the one below.

_**Output**_

```bash
❯ go run main.go
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
	       Go/channels/main.go:9 +0x48
exit status 2
```

So what is wrong? As I said earlier, think of a channel as a portal, it is just a conduit, just a passage, so if data is going in, then it’s coming out at the same time. This means our `WorldBSelf` needs to also be at the portal at the exact moment we’re sending the data through the portal so as to receive the data, otherwise we can’t send the data. Thus, both sending and receiving of data has to happen concurrently otherwise it fails and when we talk about concurrency in Golang, then we’re talking about `goroutines`. Let’s make some changes to our code.

```go
package main

import "fmt"

func main(){
	// Create our channel which we are naming portal
	portal := make(chan string)
	
	// This is the data we're sending to our other self in WorldB
	msg := "Our nemesis is coming" 

	go func() {
	// Data goes through portal
		portal <- msg
	}()

	// Data is received by our other self in World B
	worldBSelf := <- portal
	
	// We're going to print our data to see if everything is working well.
	fmt.Println(worldBSelf)
}
```

***Output***

```bash
Our nemesis is coming
```

What we’ve done now is to turn the sending of our data into a goroutine so it runs concurrently with the receiving of our data. Now when we run our data, everything should be ok because our `WorldBSelf` is also present at the portal at the exact time we’re sending the data so he can receive it.

Channels are a good way to get data from goroutines as a goroutine is another thread that is running concurrently with the main thread. You can get data from the goroutine to your main thread through channels.

Let’s go through a practical example. Let’s say you’re trying to find the sum of elements in an array.

You can divide the array into two or three, spin up goroutines to find the sum of the individual sub arrays then get the result from each goroutine and add them together to get the total sum of the array.

```go
package main 

import "fmt"

// calculate the sum of elements in an array
func calculateSum(numbers []int, channel chan int) {
	sum := 0
	for _, v := range numbers {
		sum += v
	}
	channel <- sum
}

func main(){
	// Create channel
	result:= make(chan int)
	
	array := []int{1,2,3,4,5,6,7,8,9,10}

	// Divide arrays into sub arrays
	firstSubArray, secondSubArray := array[:len(array)/2], array[len(array)/2:]

	// start goroutines
	go calculateSum(firstSubArray, result)
	go calculateSum(secondSubArray, result)

	// get result from goroutines
	x := <- result
	y := <- result
	
	// calculate total sum of the results from individual subarrays
	totalSum := x + y 
	fmt.Printf("x: %d, y: %d, total sum: %d\n", x, y, totalSum)
}

```

***Output***

```bash
X: 40, y: 15, total sum: 55
```

We divided our array into two sub arrays, we then put our calculateSum function into two goroutines, passing our sub arrays and channel as arguments. The result of those calculations are then passed through our channel to be received in the main thread and the total sum calculated.

## Buffered Channels

Remember when I said we can’t send data through channels if we’re not sending and receiving the data concurrently? Technically, we can, with buffered channels. Buffered channel is just a channel that can temporarily store data. Look at it this way, since you and your `WorldBSelf` can’t always be at the same place at the same time to transfer data, you found a more asynchronous way to do that. You found out there is a locker that is also a portal to WorldB, which means whatever you put in that locker, your `WorldBSelf` can access that artefact when they are ready. 

We can go to our very first code and add a buffer size to our channels to create this `lockerPortal`.

```go
package main

import "fmt"

func main(){
	// Create our channel which we are naming portal
	lockerPortal := make(chan string, 1) // Add buffer size to channel
	
	// This is the message we're sending to our other self in WorldB
	msg := "Our nemesis is coming" 
	
	// Message goes through portal
	lockerPortal <- msg

	// Message is received by our other self in World B
	worldBSelf := <- lockerPortal
	
	// We're going to print our message to see if everything is working well.
	fmt.Println(worldBSelf)
}
```

With these changes, our code should run ok. Note that the buffered size provided denotes the quantity of data the channel can take. A buffer size of 1 means only one data can be passed through the channel at a time.

We can try this by adding another message to the lockerPortal before reading our first data.

```go
package main

import "fmt"

func main(){
	// Create our channel which we are naming portal
	lockerPortal := make(chan string, 1) // Add buffer size to channel
	
	// This is the message we're sending to our other self in WorldB
	msg1 := "Our nemesis is coming" // renamed to msg1
	msg2 := "He is already in your World" // new code
	
	// Message goes through portal
	lockerPortal <- msg1 //renamed to msg1
	lockerPortal <- msg2 // new code

	// Message is received by our other self in World B
	worldBSelf := <- lockerPortal
	worldBSelf := <- lockerPortal
	
	// We're going to print our message to see if everything is working well.
	fmt.Println(worldBSelf)
}
```

When we run this we should have a deadlock error and the error is on the line we tried to add `msg2` because we had exceeded our buffer size. If we rearrange the lines and put `msg2` in our `lockerPortal` after receiving `msg1`, then it should work fine.

```go
package main

import "fmt"

func main(){
	// Create our channel which we are naming portal
	lockerPortal := make(chan string, 1)
	
	// This is the message we're sending to our other self in WorldB
	msg1 := "Our nemesis is coming"
	msg2 := "He is already in your World"
	
	// Send msg1 and receive it
	lockerPortal <- msg1 
	worldBSelf := <- lockerPortal
	fmt.Println(worldBSelf)
	
	// Send msg2 and receive it
	lockerPortal <- msg2 
	worldBSelf := <- lockerPortal
	fmt.Println(worldBSelf)
}
```

***Output***

```bash
Our nemesis is coming
He is already in your World
```

There are more concepts in Golang channels like closing channels and using select statements but I hope this was a good introduction to channels.