Go: on implicit type conversions, type identity and a little gotcha
A well known design decision in the Go programming language is the absence of implicit type conversions. An int
is
always an int
and not an int32
and certainly not a float
. And a net.IP
is not a []byte
… or is it?
Knowing only the “no implicit type conversions” principle, the following code should not compile:
package main
import (
"fmt"
"net"
)
func main() {
var foo net.IP
var bar []byte = []byte{1, 2, 3, 4}
foo = bar
fmt.Println(foo)
}
But it does! Now, the definition of net.IP
is type IP []byte
, but we also know that this should be an entirely new
type and not an alias, because Go doesn’t have type aliases, either. So why does this compile and execute properly?
The secret lies in Go’s understanding of type identity and assignability. The following part is of special importance:
A value x is assignable to a variable of type T (“x is assignable to T”) in any of these cases:
[…]
- x’s type V and T have identical underlying types and at least one of V or T is not a named type.
And in our case, []byte
is not a named type (unlike byte
or net.IP
, which are), which means that values of type
net.IP
and []byte
can be freely assigned to each other. And of course the same applies to all other identical
scenarios, net.IP
and []byte
are just examples.
The remaining question is: Why did they add this special case to the language specification? Go is not a language of odd exceptions, but at first glance, this exception does seem very odd. The only sensible explanation I came across are function types. Consider the following code:
package main
import "fmt"
type BinaryOp func(int, int) int
func Do(fun BinaryOp, a, b int) int {
return fun(a, b)
}
func main() {
result := Do(func(a, b int) int { return a + b },
1, 2)
fmt.Println(result)
}
If BinaryOp
(a named type) and func(int, int) int
(an unnamed type) weren’t identical, writing anonymous functions
would be quite a bit uglier.
I don’t know if the initial example, identity of named and unnamed slice types, was intentional or an accident caused by an overly broad specification, but I’d recommend not making use of it and sticking to explicit conversions, even if they’re not necessary. Clarity of code trumps saving a few keystrokes.