In a for task := range ch {..}
loop (iterating over a channel), I have several places where I want to step forward to the next element (continue
); however, I need to execute an additional statement before the continue (setting a flag to mark the worker routine is currently idle), but before the next element is received from the channel - see code sample below.
Currently, I have to repeat this statement before every continue
. This works, but is not very elegant, and a worker.setIdle()
can easily be forgotten. It would be nice if I could set up such a "finalizer" statement at the entry of the loop, something like you could expect of a for task := range ch; worker.setIdle() {...}
construct, but of course that is syntactically incorrect. It would be like a defer
in a loop, which is executed at the end of each loop iteration instead at the end of the function. Is there a way to express this in Go?
for task := range ch {
...
if (...) {
...
worker.setIdle()
continue
}
...
if (...) {
...
worker.setIdle()
continue
}
...
if (...) {
...
worker.setIdle()
continue
}
...
worker.setIdle()
}
EDIT: Thanks for the replies - I think the best solution (or at least another alternative) might be an anonymous function as follows:
for task := range ch {
func () {
...
if (...) {
...
return
}
...
} ()
worker.setIdle()
}
Only drawback is that the return statements might be misleading, they should probably be commented. A "defer" statement on basic block granularity (or also whole loop granularity) would be nice to have :-)
An easy way to achieve this to extract the body of the loop to a function and restructure your code like this:
for task := range ch {
doSomething(task)
worker.setIdle()
}
func doSomething(task Task) {
if (...) {
...
return
}
if (...) {
...
return
}
...
}
This may lead to a better seperation of concerns: One function to handle incoming tasks and the worker and on function to do the actual work.
Use a for loop without the range clause. Receive on the channel in the loop and break from the loop when the channel is closed.
for {
worker.setIdle()
task, ok := <-ch
if !ok {
break
}
if (...) {
...
continue
}
...
if (...) {
...
continue
}
...
if (...) {
...
continue
}
...
}