I have a for loop that needs to run indefinitely that has a nested loop to consume all items in a channel without waiting for the channel to close. VS Code is giving me an unreachable code error for the following Go code:
errCh := make(chan error, 5)
for {
log.Print("do stuff")
for {
select {
case err := <-errCh:
log.Printf("error occured: %s", err)
default:
break
}
}
time.Sleep(1 * time.Second)
}
VS Code is telling me that the sleep
line is unreachable, I'm guessing because it thinks that the break
statement exits the outer for loop, although I am 99% sure that break
only exits the inner for loop. Can I safely ignore this, or does VS Code know something that I don't?
Alternatively, I considered doing something like this:
errCh := make(chan error, 5)
go func() {
for err := range errCh {
log.Printf("Error occurred: %s", err)
}
}()
for {
log.Print("do stuff")
time.Sleep(1 * time.Second)
}
Would this be more idiomatic?
I have a for loop that needs to run indefinitely that has a nested loop to consume all items in a channel without waiting for the channel to close. VS Code is giving me an unreachable code error for the following Go code:
errCh := make(chan error, 5)
for {
log.Print("do stuff")
for {
select {
case err := <-errCh:
log.Printf("error occured: %s", err)
default:
break
}
}
time.Sleep(1 * time.Second)
}
VS Code is telling me that the sleep
line is unreachable, I'm guessing because it thinks that the break
statement exits the outer for loop, although I am 99% sure that break
only exits the inner for loop. Can I safely ignore this, or does VS Code know something that I don't?
Alternatively, I considered doing something like this:
errCh := make(chan error, 5)
go func() {
for err := range errCh {
log.Printf("Error occurred: %s", err)
}
}()
for {
log.Print("do stuff")
time.Sleep(1 * time.Second)
}
Would this be more idiomatic?
Share Improve this question edited Jan 30 at 16:27 Xela asked Jan 30 at 16:19 XelaXela 1191 silver badge10 bronze badges 2 |1 Answer
Reset to default -3You're correct that break only exits the inner for loop, but there's an issue with how you're trying to drain the channel inside the nested loop. The problem arises because break in a select block only breaks out of the select, not the loop itself. However, your default case immediately triggers when errCh is empty, causing the inner loop to exit every time it's evaluated.
Recommended Approach
Your second approach is far more idiomatic and preferred in Go. The issue with the first approach is that it tightly couples draining errCh with the main loop logic, making it harder to read and manage. The second approach correctly offloads error handling into a separate goroutine, ensuring that errors are processed asynchronously while your main loop continues executing.
Benefits of the second approach:
- Concurrency – The error-handling goroutine runs independently of the main loop, ensuring errors are processed as they arrive.
- Separation of Concerns – The main loop isn't cluttered with error-draining logic.
- Scalability – If the error channel receives a large number of errors quickly, the separate goroutine can handle them more efficiently than a single-threaded loop.
Additional Notes
- Since errCh is unbounded in time (i.e., there's no plan to close it in your example), the for err := range errCh goroutine will run indefinitely. Ensure there is a mechanism to close errCh if needed.
- If errCh has a high throughput and needs rate-limiting or buffering, you might consider using a worker pool pattern.
select
statement and the inner for-loop will never exit, making the sleep unreachable. 2nd is fine, assuming you plan to act, when you get an err value on the channel i.e. exit after n errors or something - tip.golang./ref/spec#Break_statements – Inian Commented Jan 30 at 16:30