DeployU
Interviews / Backend Engineering / What is the `select` statement and what is it used for in Go?

What is the `select` statement and what is it used for in Go?

conceptual Concurrency Interactive Quiz Code Examples

The Scenario

You are a backend engineer at a social media company. You are writing a new service that needs to listen for messages on multiple channels at the same time.

You need to find a way to process the messages as they come in, without blocking the other channels.

The Challenge

Explain what the select statement is in Go and how you would use it to solve this problem. What are the key benefits of using the select statement?

Wrong Approach

A junior engineer might try to solve this problem by using a separate goroutine for each channel. This would work, but it would be more complex than it needs to be.

Right Approach

A senior engineer would know that the `select` statement is the perfect tool for this job. They would be able to explain what the `select` statement is and how to use it to listen for messages on multiple channels at the same time.

Step 1: Understand What the select Statement Is

The select statement is a control structure that allows a goroutine to wait on multiple communication operations.

A select statement blocks until one of its cases can run, then it executes that case. It chooses one at random if multiple are ready.

Step 2: Use select to Listen on Multiple Channels

Here’s how we can use the select statement to listen for messages on multiple channels:

package main

import (
    "fmt"
    "time"
)

func main() {
    c1 := make(chan string)
    c2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        c1 <- "one"
    }()
    go func() {
        time.Sleep(2 * time.Second)
        c2 <- "two"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-c1:
            fmt.Println("received", msg1)
        case msg2 := <-c2:
            fmt.Println("received", msg2)
        }
    }
}

In this example, the select statement will block until a message is received on either c1 or c2.

The default Case

You can use a default case to prevent the select statement from blocking if none of the other cases are ready.

select {
case msg1 := <-c1:
    fmt.Println("received", msg1)
case msg2 := <-c2:
    fmt.Println("received", msg2)
default:
    fmt.Println("no message received")
}

Timeouts

You can use a time.After channel to implement a timeout in a select statement.

select {
case msg1 := <-c1:
    fmt.Println("received", msg1)
case <-time.After(1 * time.Second):
    fmt.Println("timeout")
}

Practice Question

You have a `select` statement with multiple cases that are ready to run. What will happen?