Go figure

Learning a new programming language is painful sometimes. I remember when I was learning Python, and I would run into language quirks all the time. It was only after suffering through a few embarrassing pull requests that I finally grokked the dang thing.

The same thing is happening to me in Go. Let’s say you have two structs that have the same field:

type A struct {
    C string
}

type B struct {
    C string
}

and then you have a list of interfaces that include two of these:

interfaces := []interface{}{A{"hello"}, B{"world"}}

In this reduced example, all I wanted to do was access C from both of these. It seemed natural to use a type switch:

for _, value := range interfaces {
    switch c := value.(type) {
    case A, B:
        fmt.Println(c.C)
    }
}

Uh oh. Go complains that c is still an interface, and as a result has no field C. That’s really strange, because this definitely works:

for _, value := range interfaces {
    switch c := value.(type) {
    case A:
        fmt.Println(c.C)
    case B:
        fmt.Println(c.C)
    }
}

So what doesn’t work with a compound case statement works just fine when you break the statement up into multiple cases. This baffled me.

It took some creative Googling to find this gem in the spec:

The TypeSwitchGuard may include a short variable declaration. When that form is used, the variable is declared at the end of the TypeSwitchCase in the implicit block of each clause. In clauses with a case listing exactly one type, the variable has that type; otherwise, the variable has the type of the expression in the TypeSwitchGuard.

In non-Golangese, this just means that a case has a single listing, the variable after switch takes on the type of the case. If the case has more than one listing, it stays its original type, which is interface{}.

I wish this type of thing would be more obvious. I expected the first pattern to be fully acceptable, or error out if compilation finds that one of the types doesn’t have a C field. It’s more expressive and avoids duplication, both strong indicators of a proper language construct. Go figure, I guess.

 
4
Kudos
 
4
Kudos

Now read this

A golang proposal

No language is perfect. Every language has warts that can’t be designed away. Recently I’ve been noticing this pattern show up a lot: thing, err := methodCouldErr() if err != nil { return err } useThing(thing) Error propagation in golang... Continue →