Breaking in Go
Jan 8, 2017 · 3 minute read · Commentsgolang
One of Go’s advantages is its familiar syntax and vocabulary. Parentheses, brackets, and braces have the meaning you expect. Programs are composed of well known keywords like var
, const
, for
, or return
. This familiarity, however, means that one might forget that Go might have slightly different semantic for those terms.
One such example is the keyword break
. Recently, we wrote a program which boiled down to an equivalent of:
for i := 0; i < 10; i++ {
fmt.Println("Line:", i)
switch i {
case 5:
break
}
}
The desired output was to terminate the loop when a condition was reached inside the switch statement. This code instead prints all ten lines. As the Go language specification states break
terminates innermost for
, switch
, or select
.
OK, that’s not the desired behaviour, let’s fix it and tell Go to terminate the loop. An approach natural to anyone who has programmed in PHP is to add the number of levels the break
should terminate:
for i := 0; i < 10; i++ {
fmt.Println("Line:", i)
switch i {
case 5:
break 2
}
}
This doesn’t compile! I didn’t check if this is a speciality of PHP or if there are other languages which understand this expression. In any case, it’s point illustrating that apparent similarities might hide semantic differences.
The correct Go approach is to specify a label and then say that the execution should continue at that level:
L:
for i := 0; i < 10; i++ {
fmt.Println("Line:", i)
switch i {
case 5:
break L
}
}
In essence, it is a special case of goto
statement which has had bad reputation ever since structured programming became a thing. It’s also interesting to note that there are languages which don’t have a goto
and the same problem would have be to solved by introducing a boolean variable:
cont := true
for i := 0; cont && i < 10; i++ {
fmt.Println("Line:", i)
switch i {
case 5:
cont = false
break
}
}
or wrapping the whole loop in a function:
func () {
for i := 0; i < 10; i++ {
fmt.Println("Line:", i)
switch i {
case 5:
return
}
}
}()
Looking at the alternatives, a masked goto
is a reasonable solution. Though, I wouldn’t be surprised if people kept avoiding it purely because “goto
is evil”.
As a side note, it’s worth mentioning that the code at the beginning came to be because the actual condition was originally a much more complex if
which was refactored into a more readable switch
. However, this refactoring meant that break
was pushed one level too deep.
We're looking for developers to help us save energy
If you're interested in what we do and you would like to help us save energy, drop us a line at jobs@enectiva.cz.