Unless there's another branch of the select, use the following:

Copyfor v := range ch {
    // do some stuff
}

The code is simpler and easier to understand than the for/select presented in the question.

If you need to do the receive inside the loop for some reason, then use the following code:

Copy for  { 
     // do some stuff
     v, ok := <-ch
     if !ok { 
        break 
     } 
     // do some other stuff
 }

As a rule of thumb, single branch select statements should be avoided. A select with a single branch is functionally the same as the branch alone.

Answer from user5728991 on Stack Overflow
🌐
Devtrovert
blog.devtrovert.com › select & for range channel in go: breaking down
Select & For Range Channel in Go: Breaking Down - Devtrovert
March 12, 2024 - By doing this, you avoid keeping the loop running for no reason, and you manage channel activity in a clean, efficient way. ... Without setting ch1 to nil, the output continuously displays Received from ch1, and it keeps repeating because it’s no longer receiving meaningful data, just the default over and over. This continues until ch2 closes and timeout happens. Using for range with channels in Go is quite handy, instead of repeatedly checking if there’s a new value in the channel, now you can just sit back and let the loop handle it.
Discussions

Question about ranging over channels
you should use the range option. the first example will continue to print "" after the channel is closed on every iteration of the loop due to the behavior of closed channels . the range option works as expected since "the iteration values produced are the successive values sent on the channel until the channel is closed" from the range clause spec . More on reddit.com
🌐 r/golang
4
6
September 11, 2017
goroutine - Selecting inside range over go channel - Stack Overflow
I'm following this post to parallelise my app. I need to tailor this code: func sq(done More on stackoverflow.com
🌐 stackoverflow.com
Question: multiple channels + select vs one channel of interface
A priori, if you have n operations that can be executed without blocking each other you should probably use a sync.WaitGroup along with n different go routines each listening to one of these n different channels for maximum performance and extendability. On the other hand, if you’re reading n channels in a for-select statement how do you deal with closed channels? Note that case v, ok := <- closedCh: if !ok { continue } ... is entered very often and thus raises a bad performance. That's why one usually merges n channels into one. This single channel is then closed as soon as the last of n channels has been closed. More on reddit.com
🌐 r/golang
10
6
January 11, 2021
Beginner question about channels
If you do this package main import "fmt" func main() { queue := make(chan string, 2) queue <- "one" queue <- "two" for elem := range queue { fmt.Println(elem) } close(queue) } Then the for loop never knows to stop (it will stop when the channel it ranges over is closed and it has completed "draining" the channel, but the close channel command is never reached so the loop sits there forever.) package main import ( "fmt" "time" ) func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { fmt.Println("worker", id, "started job", j) time.Sleep(time.Second) fmt.Println("worker", id, "finished job", j) results <- j * 2 } } func main() { const numJobs = 5 jobs := make(chan int, numJobs) results := make(chan int, numJobs) for w := 1; w <= 3; w++ { go worker(w, jobs, results) } for j := 1; j <= numJobs; j++ { jobs <- j } close(jobs) for a := 1; a <= numJobs; a++ { <-results } } Here the jobs channel close command is reached because the loop immediately before it is limited by the numJobs variable (so there's no infinite loop there) The loop inside the worker function will eventually stop because the close command for jobs is in a different goroutine, meaning that the worker loop doesn't prevent the close from happening. Edit: The easiest way to think about this is to think of it like this. In the first example the goroutine worked its way through the code, and started the loop that went forever waiting for someone to tell it to stop looping. The command to stop was the thing that it was supposed to do after the loop finished, so it could never get there. Now, the second example, the worker goroutine was sent off to do some work by the main/boss goroutine, and it went into a loop that will stop when the channel closes, it can never close the channel, and there is no other way for the loop to end. However the main goroutine, the one that sent the worker off, it has one loop to execute, that is limited by the numJobs variable, and then it closes the jobs channel, telling the worker goroutine that the loop it is in can finish. More on reddit.com
🌐 r/golang
9
3
August 31, 2023
🌐
WONJOON.LOG
wnjoon.github.io › 2025 › 03 › 09 › go-channel-iteration
Understanding Channel Iteration in Go: for range vs. select vs ...
March 9, 2025 - Use for range when working with a single channel that continuously receives values. Use select when handling multiple channels or managing timeouts.
🌐
Smartscribs
smartscribs.com › home › golang concurrency: select and for range channel
Golang Concurrency: Select and For Range Channel - Smartscribs
December 22, 2023 - There is a common trap, if you forget to close the channel, the loop will just keep waiting and it’ll be stuck, waiting for new values forever (deadlock). So, if you’re running a bunch of goroutines, this mistake can eat up memory pretty fast. So there were some of the usages, common errors and patterns to use the select & for range statement with channnels in go. I hope you are enjoying this series on golang and concurreny.
🌐
Reddit
reddit.com › r/golang › question about ranging over channels
r/golang on Reddit: Question about ranging over channels
September 11, 2017 -

Hey all! I am writing a function that will read messages sent to a channel and used like this:

go readMessages(messenger)

Where messenger is the channel of string messages.

I am confused about the difference between this:

func readMessages(messenger <-chan string) {
	for {
		select {
		case msg := <-messenger:
			fmt.Println(msg)
		}
	}
}

and this:

func readMessages(messenger <-chan string) {
	for msg := range messenger {
		fmt.Println(msg)
	}
}

Are they equivalent? Or Should I prefer one over the other and why?

Top answer
1 of 2
1
  1. Being in a range or not doesn't have any impact on what select is doing here.

  2. No, select doesn't take the first true expression... it doesn't take expressions at all. The only things that can appear as the cases of an expression are channel sends, channel receives, and assignments with channel receives on their right side.

    select {
    case out <- n * n:
    case <-done:
        return
    }
    

says "if sending on out is possible (i.e. it has remaining capacity or an active reader), then send the value n * n to it and continue. If receiving from done is possible, return from the function. If both are possible, choose one at random and do it. If neither is possible, wait until one of them becomes possible." (See Select Statements in the spec).

If the value you want to send needs to be computed (and it's too complex to put on the right hand side of the channel send), simply do it before the select. The spec makes it clear that all of the expressions in send statements in a select are computed ahead of time anyway, so nothing is lost.

2 of 2
0

I don't fully understand the line case out <- n * n:. I can see it's saying that if there's a value for n, then square it and send it down the channel, but I don't understand why.

That's not correct. case out <- n * n checks to see if out is ready to read, and sends n * n to out if it is. Unless done is also ready.

select is used when you have multiple channels to talk to. Whichever channel is ready, it will do that case. If multiple channels are ready, it will select one at random.

select {
    case out <- n * n:
    case <-done:
        return
    }
}

This will select over out and done. If either is ready to proceed, ie. out is ready to read or there's something to read from done, it will pick one of those cases. The order is random, so it is possible to send more down out even if there's something to be read from done.

This pattern is used to shut down infinite goroutines. If you stop reading from its output channel, it won't do any more work, but it will hang around in memory. So by passing a value to done you can tell the goroutine to shut down.


UPDATE: In your original case, where the goroutine is looping over an input channel and sending output, done is an unnecessary complication. Once the input channel is closed, the function will return.

func sq(in <-chan int) <-chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        for n := range in {
            out <- n * n
        }
    }()
    return out
}

func main() {
    in := make(chan int)
    out := sq(in)
    for _,i := range []int{1,2,3,4} {
        in <- i
        fmt.Println(<-out)
    }

    // The `range` inside the goroutine from sq() will exit,
    // and the goroutine will return.
    close(in)
}

If it just spat out an ever increasing set of squares, then done would be necessary inside an infinite loop.

func sq(done chan bool) <-chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        n := 0
        for {
            select {
                case <-done:
                    return
                case out<-n*n:
                    n++
            }
        }
    }()
    return out
}

func main() {
    done := make(chan bool)
    out := sq(done)
    for range []int{1,2,3,4} {
        fmt.Println(<-out)
    }

    // The switch in the goroutine will be able to read
    // from done (out's buffer being already full) and return.
    done <- true
}
🌐
Blogger
golangtutorials.blogspot.com › 2011 › 06 › channels-in-go-range-and-select.html
GoLang Tutorials: Channels in Go - range and select
June 11, 2011 - But by using the range keyword on the channel in the code above, we avoid that need - now when the channel is closed, the for loop automatically stops. The select keyword when used in conjunction with many channels works like a are-you-ready polling mechanism across different channels.
Find elsewhere
🌐
Reddit
reddit.com › r/golang › question: multiple channels + select vs one channel of interface
r/golang on Reddit: Question: multiple channels + select vs one channel of interface
January 11, 2021 -

Suppose you have a long-running goroutine that accepts different operations. In my case, it is a routine that listens for different events and reacts to them. I am using a single coroutine because the events are not independent and the reactions depend on an internal state (and I can avoid locks) and it's a pretty short code to have it in a single function.

My question, I can create a channel for each operation and use select in the goroutine or use a single channel of a common interface and dispatch by message type.

The only advantage I see in the single channel approach is that messages are dispatched on the order they are received, which can be important on some scenarios, but in my case it is not.

Do you have any experience or opinion about which approach is better?

Thanks!

🌐
Golangmastery
golangmastery.github.io › courses › concurrent-programming-with-go › 12-range-over-channels
Range over Channels | Golang Mastery
This makes range particularly useful ... uses range to process jobs: ... When using range with select, you need a different approach since range is a loop and select is a block....
🌐
Go
go.dev › tour › concurrency › 4
Range and Close
A sender can close a channel to indicate that no more values will be sent. Receivers can test whether a channel has been closed by assigning a second parameter to the receive expression: after ... The loop for i := range c receives values from the channel repeatedly until it is closed.
🌐
Go 101
go101.org › article › channel.html
Channels in Go -Go 101
The loop will try to iteratively receive the values sent to a channel, until the channel is closed and its value buffer queue becomes blank. With for-range syntax on arrays, slices and maps, multiple iteration variables are allowed.
🌐
Go by Example
gobyexample.com › range-over-channels
Go by Example: Range over Channels
In a previous example we saw how for and range provide iteration over basic data structures. We can also use this syntax to iterate over values received from a channel · We’ll iterate over 2 values in the queue channel
🌐
Go by Example
gobyexample.com › select
Go by Example: Select
Go’s select lets you wait on multiple channel operations. Combining goroutines and channels with select is a powerful feature of Go · For our example we’ll select across two channels
🌐
Go
go.dev › tour › concurrency › 5
select statement
Buffered Channels · Range and Close · Select · Default Selection · Exercise: Equivalent Binary Trees · Exercise: Equivalent Binary Trees · sync.Mutex · Exercise: Web Crawler · Where to Go from here... The select statement lets a goroutine wait on multiple communication operations.
🌐
Better Programming
betterprogramming.pub › manging-go-channels-with-range-and-close-98f93f6e8c0c
How to Manage Go Channels With Range and Close | by Abhishek Gupta | Better Programming
April 22, 2020 - The consumer goroutine doesn’t have to coexist with the producer goroutine to receive the values — i.e. even if the producer goroutine finishes (and closes the channel), the consumer goroutine range loop will receive all the values.
🌐
Psj
psj.codes › concurrency-in-go-part-1-goroutines-channels-and-select
Concurrency in Go (Part-1): Goroutines, Channels and Select
August 28, 2023 - The important thing to notice is ... otherwise for loop will keep on waiting forever. In Go, the select statement is like a switch statement but for channels....
🌐
Reddit
reddit.com › r/golang › beginner question about channels
r/golang on Reddit: Beginner question about channels
August 31, 2023 -

Lately I've been learning go and I'm really liking it so far. I think Go fits the needs of a JavaScript programmer that wants to go lower level but not as low as c/c++.Currently I'm using Go by example for learning purposes.

I was learning channels and it's taking me a while to wrap my head around it.

I'm not understanding why the following code with channels works they way it does.

Part 1 taken from Range over channels section

Part 2 taken from Worker pools section

I modified the Part 1 and instead of closing the channel before iterating over it, I closed it after the iteration, resulting in a "all goroutines are asleep - deadlock!" error message in runtime. It looks like you cant iterate over a channel that's not closed. Fair enough.

Then I stumbled upon worker pools and in the example the worker is iterating over jobs channel even though its not closed?

Clearly my understanding of channels is not correct.

Why is Part 2 working well and the modified version of Part 1 (iterate over a channel that's not closed) is not?

Thank you in advance

Top answer
1 of 5
6
If you do this package main import "fmt" func main() { queue := make(chan string, 2) queue <- "one" queue <- "two" for elem := range queue { fmt.Println(elem) } close(queue) } Then the for loop never knows to stop (it will stop when the channel it ranges over is closed and it has completed "draining" the channel, but the close channel command is never reached so the loop sits there forever.) package main import ( "fmt" "time" ) func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { fmt.Println("worker", id, "started job", j) time.Sleep(time.Second) fmt.Println("worker", id, "finished job", j) results <- j * 2 } } func main() { const numJobs = 5 jobs := make(chan int, numJobs) results := make(chan int, numJobs) for w := 1; w <= 3; w++ { go worker(w, jobs, results) } for j := 1; j <= numJobs; j++ { jobs <- j } close(jobs) for a := 1; a <= numJobs; a++ { <-results } } Here the jobs channel close command is reached because the loop immediately before it is limited by the numJobs variable (so there's no infinite loop there) The loop inside the worker function will eventually stop because the close command for jobs is in a different goroutine, meaning that the worker loop doesn't prevent the close from happening. Edit: The easiest way to think about this is to think of it like this. In the first example the goroutine worked its way through the code, and started the loop that went forever waiting for someone to tell it to stop looping. The command to stop was the thing that it was supposed to do after the loop finished, so it could never get there. Now, the second example, the worker goroutine was sent off to do some work by the main/boss goroutine, and it went into a loop that will stop when the channel closes, it can never close the channel, and there is no other way for the loop to end. However the main goroutine, the one that sent the worker off, it has one loop to execute, that is limited by the numJobs variable, and then it closes the jobs channel, telling the worker goroutine that the loop it is in can finish.
2 of 5
3
It looks like you cant iterate over a channel that's not closed. Fair enough. Sure you can. If you range over a channel, the loop will block until (1) there's something to receive, and then run the body, or (2) the channel is closed, and then proceed after the body. That's all. I closed it after the iteration, resulting in a "all goroutines are asleep - deadlock!" It's indicating to you that no progress can be made. Since the channel is not closed, the for loop is blocked waiting for (1) another element, or (2) the channel is closed, as above. Since there's nothing else that's capable of doing either 1 or 2, you have a deadlock.
🌐
DEV Community
dev.to › itnext › how-to-manage-go-channels-using-range-and-close-2k7f
How to manage Go channels using range and close - DEV Community
May 8, 2020 - close makes it possible to signal to consumers of a channel that there is nothing else which will be sent on this channel · Let's refactor the program. First, change the consumer to use range - remove the i := <-c bit and replace it with for i := range c