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.