A Tricky Bug with Interfaces and Switch Cases in Go

Watch the video about this bug on the Golang Study Group channel (in Portuguese).

Golang is an easy language to program in, and it has few traps for the programmer. Most things that can go wrong are caught by the compiler or by one of the many static analysis tools that help keep our code almost bug free.

But there are situations where even those tools have a hard time warning us, because there is no way to know what the programmer expected the program to do. This usually happens when you use the more entertaining features of the language, such as switch, interfaces, channels, and goroutines.

The switch case order bug

Here we are going to look at a bug that is really hard to spot: the order of the cases in a switch is wrong. The broadest case comes first, so the switch always stops there. To fix it, all you have to do is reorder the cases, putting the narrower ones at the top. But by then you have already burned a lot of time debugging and several liters of coffee. You need to understand how the language features work, because no compiler can catch every possible mistake an inspired programmer might make.

Take a look at the example below.

package main

import "fmt"

type comptometer interface {
    Sum(a, b int) int
}

type foo struct{}

func (_ foo) Sum(a, b int) int {
    return a + b
}

type bar struct{}

func (_ bar) Sum(a, b int) int {
    return a + b
}

//
func printType(e comptometer) {
    /*
        This switch is in the wrong order
        and will always print "comptometer
        interface", never reaching the
        other cases. Move the comptometer
        case to the end of the switch
        to fix the bug.
    */
    switch e.(type) {
    case comptometer:
        fmt.Println("comptometer interface")
    case *foo:
        fmt.Println("pointer to foo")
    case *bar:
        fmt.Println("pointer to bar")
    }
}

func main() {
    var f = &foo{}
    printType(f)
    var b = &bar{}
    printType(b)
}

The switch statement tests each case in the order they appear and jumps to the first one that matches. In the example, the comptometer interface always satisfies the condition, because both structs implement the function the interface expects.

You can see the source code for this example in our Golang Study Group repository.

Cesar Gimenes

Last modified
Tags: