This article will list all the value comparison, conversion and comparison rules in Go. Please note that type parameter types (used frequently in custom generics) are deliberately ignored in the descriptions of conversion, assignability and comparison rules. In other words, this book doesn't consider the situations in which custom generics are involved.
In Go, if a value v
can be explicitly converted to type T
,
the conversion can be represented as the form (T)(v)
.
For most cases, in particular T
is a type name (an identifier),
the form can be simplified to T(v)
.
One fact we should know is,
when it says a value x
can be implicitly converted to a type T
,
then it means x
can also be explicitly converted to type T
.
byte
and uint8
can be converted to each other.
rune
and int32
can be converted to each other.
[]byte
and []uint8
can be converted to each other.
Nothing more to explain about this rule, whether you think this case involves conversions or not.
x
and a non-interface type T
, assume the type of x
is Tx
,
Tx
and T
share the same underlying type (ignoring struct tags),
then x
can be explicitly converted to T
.
Tx
or T
is a unnamed type
and their underlying types are identical (considering struct tags),
then x
can be implicitly converted to T
.
Tx
and T
have different underlying types,
but both Tx
and T
are unnamed pointer types
and their base types share the same underlying type (ignoring struct tags),
then x
can be explicitly converted to T
.
(Note, the two ignoring struct tags occurrences have taken effect since Go 1.8.)
package main
func main() {
// []int, IntSlice and MySlice share
// the same underlying type: []int
type IntSlice []int
type MySlice []int
type Foo = struct{n int `foo`}
type Bar = struct{n int `bar`}
var s = []int{}
var is = IntSlice{}
var ms = MySlice{}
var x map[Bar]Foo
var y map[Foo]Bar
// The two implicit conversions both doesn't work.
/*
is = ms // error
ms = is // error
*/
// Must use explicit conversions here.
is = IntSlice(ms)
ms = MySlice(is)
x = map[Bar]Foo(y)
y = map[Foo]Bar(x)
// Implicit conversions are okay here.
s = is
is = s
s = ms
ms = s
}
Pointer related conversion example:
package main
func main() {
type MyInt int
type IntPtr *int
type MyIntPtr *MyInt
var pi = new(int) // the type of pi is *int
// ip and pi have the same underlying type,
// and the type of pi is unnamed, so
// the implicit conversion works.
var ip IntPtr = pi
// var _ *MyInt = pi // can't convert implicitly
var _ = (*MyInt)(pi) // ok, must explicitly
// Values of *int can't be converted to MyIntPtr
// directly, but can indirectly.
/*
var _ MyIntPtr = pi // can't convert implicitly
var _ = MyIntPtr(pi) // can't convert explicitly
*/
var _ MyIntPtr = (*MyInt)(pi) // ok
var _ = MyIntPtr((*MyInt)(pi)) // ok
// Values of IntPtr can't be converted to
// MyIntPtr directly, but can indirectly.
/*
var _ MyIntPtr = ip // can't convert implicitly
var _ = MyIntPtr(ip) // can't convert explicitly
*/
var _ MyIntPtr = (*MyInt)((*int)(ip)) // ok
var _ = MyIntPtr((*MyInt)((*int)(ip))) // ok
}
x
, assume its type Tx
is a
bidirectional channel type, T
is also a channel type (bidirectional or not).
If Tx
and T
have the identical element type,
and either Tx
or T
is an unnamed type,
then x
can be implicitly converted to T
.
package main
func main() {
type C chan string
type C1 chan<- string
type C2 <-chan string
var ca C
var cb chan string
cb = ca // ok, same underlying type
ca = cb // ok, same underlying type
// The 4 lines compile okay for this 3rd rule.
var _, _ chan<- string = ca, cb // ok
var _, _ <-chan string = ca, cb // ok
var _ C1 = cb // ok
var _ C2 = cb // ok
// Values of C can't be converted
// to C1 and C2 directly.
/*
var _ = C1(ca) // compile error
var _ = C2(ca) // compile error
*/
// Values of C can be converted
// to C1 and C2 indirectly.
var _ = C1((chan<- string)(ca)) // ok
var _ = C2((<-chan string)(ca)) // ok
var _ C1 = (chan<- string)(ca) // ok
var _ C2 = (<-chan string)(ca) // ok
}
x
and an interface type I
,
if the type (or the default type) of x
is Tx
and Tx
implements I
,
then x
can be implicitly converted to type I
.
The conversion result is an interface value (of type I
),
which boxes
x
, if Tx
is a non-interface type;x
, if Tx
is an interface type.Please read interfaces in Go for details and examples.
T
,
if the untyped value can represent as values of type T
.
package main
func main() {
var _ []int = nil
var _ map[string]int = nil
var _ chan string = nil
var _ func()() = nil
var _ *bool = nil
var _ interface{} = nil
var _ int = 123.0
var _ float64 = 123
var _ int32 = 1.23e2
var _ int8 = 1 + 0i
}
(This rule is some overlapped with the last one.)
Generally, converting a constant still yields a constant as result (except that the target type is not a basic type).
x
and a basic type T
, if x
is representable as a value of type T
,
then x
can be explicitly converted to T
. In particular if x
is an untyped value,
then x
can be implicitly converted to T
.
package main
func main() {
// The implicit conversions are all legal.
const I = 123
const I1, I2 int8 = 0x7F, -0x80
const I3, I4 int8 = I, 0.0
const F = 0.123456789
const F32 float32 = F
const F32b float32 = I
const F64 float64 = F
const C1, C2 complex64 = F, I
// const F64b float64 = I3 // doesn't compile
const F64b = float64(I3) // compiles okay
// const I5 int = C2 // doesn't compile
const I5 = int(C2) // compiles okay
}
package main
import "fmt"
func main() {
var a, b = 1.6, -1.6 // both are float64
fmt.Println(int(a), int(b)) // 1 -1
var i, j int16 = 0x7FFF, -0x8000
fmt.Println(int8(i), uint16(j)) // -1 32768
var c1 complex64 = 1 + 2i
var _ = complex128(c1)
}
[]byte
(a.k.a., []uint8
),
and vice versa.
[]rune
(a.k.a., []int32
),
and vice versa.
Please read strings in Go for details and examples.
Here is an example.
Here is an example.
unsafe.Pointer
, and vice versa.
unsafe.Pointer
, and vice versa.
Please read type-unsafe pointers in Go for details and examples.
Assignments can be viewed as implicit conversions. Implicit conversion rules are listed among all conversion rules in the last section.
Besides these rules, the destination values in assignments must be addressable values, map index expressions, or the blank identifier.
In an assignment, the source value is copied to the destination value. Precisely speaking, the direct part of the source value is copied to the destination value.
Note, parameter passing and result returning are both value assignments actually.
Go specification states:
So, the comparison rule is much like the assignment rule. In other words, two values are comparable if one of them can be implicitly converted to the type of the other. Right? Almost, for there is another rule which has a higher priority than the above basic comparison rule.
By the above rule, if an incomparable type (which must be a non-interface type) implements an interface type, then it is illegal to compare values of the two types, even if values of the former (non-interface) type can be implicitly converted to the latter (interface) type.
Note, although values of slice/map/function types don't support comparisons,
they can be compared with untyped nil values (a.k.a., bare nil
identifiers).
The results of comparing two untyped numeric values obey intuition.
Note, an untyped nil value can't be compared with another untyped nil value.
Any comparison results in an untyped boolean value.
The following example shows some incomparable types related comparisons.package main
// Some variables of incomparable types.
var s []int
var m map[int]int
var f func()()
var t struct {x []int}
var a [5]map[int]int
func main() {
// The following lines fail to compile.
/*
_ = s == s
_ = m == m
_ = f == f
_ = t == t
_ = a == a
_ = nil == nil
_ = s == interface{}(nil)
_ = m == interface{}(nil)
_ = f == interface{}(nil)
*/
// The following lines compile okay.
_ = s == nil
_ = m == nil
_ = f == nil
_ = 123 == interface{}(nil)
_ = true == interface{}(nil)
_ = "abc" == interface{}(nil)
}
T
.
(If they have different types, one of them must be implicitly convertible to the type of the other.
Here we don't consider the cases in which both the two values are untyped.)
T
is a boolean type,
then the two values are equal only if they are both true
or both false
.
T
is an integer type,
then the two values are equal only if they have the same representation in memory.
T
is a floating-point type,
then the two values are equal only if any of the following conditions is satisfied:
+Inf
.
-Inf
.
-0.0
or +0.0
.
NaN
and they have the same bytes representations in memory.
T
is a complex type,
then the two values are equal only if their real parts (as floating-point values)
and imaginary parts (as floating-point values) are both equal.
T
is a pointer type (either safe or unsafe),
then the two values are equal only if the memory addresses stored in them are equal.
T
is a channel type, the two channel values are equal
if they both reference the same underlying internal channel structure value
or they are both nil channels.
T
is a struct type, then
each pair of the corresponding
fields of the two struct values will be compared.
T
is an array type, then
each pair of the corresponding
elements of the two array values will be compared.
T
is an interface type, please read
how two interface values are compared.
T
is a string type, please read
how two string values are compared.
package main
func main() {
type T struct {
a interface{}
b int
}
var x interface{} = []int{}
var y = T{a: x}
var z = [3]T{{a: y}}
// Each of the following line can produce a panic.
_ = x == x
_ = y == y
_ = z == z
}
The Go 101 project is hosted on Github. Welcome to improve Go 101 articles by submitting corrections for all kinds of mistakes, such as typos, grammar errors, wording inaccuracies, description flaws, code bugs and broken links.
If you would like to learn some Go details and facts every serveral days, please follow Go 101's official Twitter account @zigo_101.
reflect
standard package.sync
standard package.sync/atomic
standard package.nil
in Go