Send me an email!
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.