Introduction
What are channels ?
channel is a communications pipe between goroutines. Things go in one end and come out another in the same order until the channel is closed.

Creating Channels
func main() {
var ch chan string
fmt.Println(c)
}go run main.go
<nil>Warning
As we can see, the zero value of a channel is
niland if we try to send data over the channel our program will panic.
func main() {
ch := make(chan string)
fmt.Println(c)
}output:
$ go run main.go
0x1400010e060channel was initialized.
Sending and Receiving data
package main
import "fmt"
func speak(arg string, ch chan string) {
ch <- arg // Send
}
func main() {
ch := make(chan string)
go speak("Hello World", ch)
data := <- ch // Receive
fmt.Println(data)
}output:
$ go run main.go
Hello WorldNotice how we can send data using the channel <- data and receive data using the data := <- channel syntax.
Buffered Channels
Buffered channels that accept a limited number of values without a corresponding receiver for those values.

func speak(arg string, ch chan string) {
ch <- arg // Send
}
func main() {
ch := make(chan string, 2) //2 <-- Buffer length
go speak("Hello World", ch)
go speak("Hi again", ch)
data1 := <- ch
fmt.Println(data1)
data2 := <- ch
fmt.Println(data2)
}Directional Channels
When using channels as function parameters, we can specify if a channel is meant to only send or receive values. This increases the type-safety of our program as by default a channel can both send and receive values.

func speak(arg string, ch chan <- string) {
ch <- arg // Send Only
}// Function that receives a value from a channel
func listen(ch <- chan string) {
// Receive a value from the channel and print it
message := <- ch // Receive Only
fmt.Println("Received:", message)
}Closing Channels
func main() {
ch := make(chan string, 2)
go speak("Hello World", ch)
go speak("Hi again", ch)
data1 := <- ch
fmt.Println(data1)
data2 := <- ch
fmt.Println(data2)
close(ch) // close ch resource
}func main() {
ch := make(chan string, 2)
go speak("Hello World", ch)
go speak("Hi again", ch)
data1 := <- ch
fmt.Println(data1)
data2, ok := <- ch
fmt.Println(data2, ok)
close(ch)
}if ok is false then there are no more values to receive and the channel is closed.
Important
closing a channel only means “no more values will be sent to it.”
Reading from a closed channel is still safe.
Channel Properties
- A send to a
nilchannel blocks forever.
var c chan string
c <- "Hello, World!" // Panic: all goroutines are asleep - deadlock!- A receive from a
nilchannel blocks forever.
var c chan string
fmt.Println(<- c) // Panic: all goroutines are asleep - deadlock!- A send to a closed channel causes a panic.
var c = make(chan string, 1)
c <- "Hello, World!"
close(c)
c <- "Hello, Panic!" // Panic: send on closed channel- A receive from a closed channel returns the zero value immediately.
var c = make(chan int, 2)
c <- 5
c <- 4
close(c)
for i := 0; i < 4; i++ {
fmt.Printf("%d ", <-c) // Output: 5 4 0 0
}- Range over channels.
We can also use for and range to iterate over values received from a channel.
package main
import "fmt"
func main() {
ch := make(chan string, 2)
ch <- "Hello"
ch <- "World"
close(ch)
for data := range ch {
fmt.Println(data)
}
}Select Statement
The select statement blocks the code and waits for multiple channel operations simultaneously.
A select blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready.
package main
import (
"fmt"
"time"
)
func main() {
one := make(chan string)
two := make(chan string)
go func() {
time.Sleep(time.Second * 2)
one <- "One"
}()
go func() {
time.Sleep(time.Second * 1)
two <- "Two"
}()
select {
case result := <-one:
fmt.Println("Received:", result)
case result := <-two:
fmt.Println("Received:", result)
}
close(one)
close(two)
}