This article will list all the value assignment, conversion and comparison rules in Go.
Note, the definition of conversion in Go 101 is not exactly the same as Go spec. The conversion in Go spec means explicit conversion. The conversions in Go 101 include both explicit and implicit conversions.
In Go, if a value v can be explicitly converted to type T,
the conversion can be achieveed with the form (T)(v).
Sometimes, in particular T is a named type (a defined type or a type alias),
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.
T1 and T2 denote the identical type,
then their values can be implicitly converted each other.
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,
in particular if either Tx or T is not a defined type
and they 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
non-defined pointer types
and their base types share the same underlying type (ignoring struct tags),
then x can (and must) 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
var s = []int{}
var is = IntSlice{}
var ms = MySlice{}
var x struct{n int `foo`}
var y struct{n int `bar`}
// The two lines both fail to compile.
/*
is = ms
ms = is
*/
// Must use explicit conversions here.
is = IntSlice(ms)
ms = MySlice(is)
x = struct{n int `foo`}(y)
y = struct{n int `bar`}(x)
// Implicit conversions are okay here.
s = is
is = s
s = ms
ms = s
}
package main
func main() {
type MyInt int
type IntPtr *int
type MyIntPtr *MyInt
var pi = new(int) // type is *int
var ip IntPtr = pi // ok, same underlying type
// var _ *MyInt = pi // can't convert implicitly
var _ = (*MyInt)(pi) // ok, must explicitly
// var _ MyIntPtr = pi // can't convert implicitly
// var _ = MyIntPtr(pi) // can't convert explicitly
var _ MyIntPtr = (*MyInt)(pi) // ok
var _ = MyIntPtr((*MyInt)(pi)) // ok
// 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
}
Tx is a bidirectional channel type, T is a channel type,
Tx and T have the identical element type,
and either Tx or T is not an defined 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
var _, _ chan<- string = ca, cb // ok
var _, _ <-chan string = ca, cb // ok
var _ C1 = cb // ok
var _ C2 = cb // ok
// var _ = C1(ca) // compile error
// var _ = C2(ca) // compile error
var _ = C1((chan<- string)(ca)) // ok
var _ = C2((<-chan string)(ca)) // ok
}
x and an interface type I, assume the type (or the defaut type) of x is Tx,
if Tx implements I, then x can be implicitly converted to type I.
The conversion result is an interface value (of type I), which stores
x, if Tx is a non-interface type;x, if Tx is an interface type.package main
type Filter interface {
Filte(n int) bool
}
type MultipleFilter struct {
Divisor int
}
func (mf MultipleFilter) Filte(n int) bool {
return n % mf.Divisor == 0
}
func main() {
var mf = MultipleFilter{11}
// mf's type MultipleFilter implements Filter,
// so here implicit conversion is okay.
var filter = mf
// all types implement interface{} type, so any
// value can be converted to interface{} implicitly.
var n int = 123
var b bool = true
var s string = "abc"
var _ interface{} = n
var _ interface{} = b
var _ interface{} = s
var _ interface{} = mf
// interface type Filter also implements interface{}
var _ interface{} = filter
}
Many parameters of the functions in standard packages are
of type interface{}.
As all types implement interface{}, so any value
can be passed for such parameters without explict conversion.
(Type switch can be viewed as a special form of type assertion.)
x and a non-interface type T,
assume the interface type of x is Ix,
if T implements Ix, then x
can be converted to T with type assertion syntax:
x and T are exactly the same type,
then the conversion result is the dynamic value of x.
If the ok result value is present, its value is true.
T
if the ok result value is present (the returned ok value is false).
The conversion will panic if the ok result value is not present.
x and an interface type I,
assume the interface type of x is Ix,
then x can be converted to I with type assertion syntax,
whether I implements Ix or not:
x is not a nil interface and
the dynamic type of x implements I,
then the conversion result (a value of I)
stores (a copy of) the dynamic value of x.
If the ok result value is present, its value is true.
false).
The conversion will panic if the ok result value is not present.
package main
type I interface {
f()
}
type T string
func (T) f(){}
func main() {
var it interface{} = T("abc")
var is interface{} = "abc"
// the two are both okay, for T
// implements both I and interface{}
var _ T = it.(T)
var _ I = it.(I)
// this one is also okay, for
// string implements interface{}
var _ string = is.(string)
// following 3 compile okay but will
// all panic at run time.
// _ = is.(I)
// _ = is.(T)
// _ = it.(string)
}
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.)
Converting a constant yields a typed constant as result.
x and a type T, if x is representable by a value of type T,
then x can be explicitly converted to T, in particular if x is an untyped
or its type is also T, then x can be implicitly converted to T.
Note, in case of x is a floating-point constant and T is a floating-point type,
the conversion result T(x) is the rounded value of x by using IEEE 754 round-to-even rules,
but with an IEEE -0.0 further rounded to an unsigned 0.0.
package main
func main() {
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 F64b = float64(I3)
const C1, C2 complex64 = F, I
const I5 = int(C2)
}
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)
}
Below, string types means the types whose underlying types are the built-in string type, and integer types means the types whose underlying types are the built-in integer types,
[]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.
Above conversion rules can be broken by unsafe.Pointer related conversion rules.
unsafe.Pointer, and vice versa.
uintptr value can be converted to a type whose underlying type is unsafe.Pointer, and vice versa.
Please read type-unsafe pointers in Go for details and examples.
Go specification says:
Except the above case, assignments can be viewed as implicit conversions.
Implicit conversion rules are listed among the conversion rules in the last section.
Note, parameter passing, channel value send and receive operations are all value assignment involved.
For most value assignments, the direct part of each source value is copied to the direct part of the corresponding destination value, except that destination value is an interface value.
In an assignment, if the destination value is an interface value but the source value is not, then (the direct part of) the source value will be copied and the copy result will be stored in the destination interface value as dynamic value. The standard compiler makes some special treatments here if the source value is not a pointer value. If the source value is not a pointer value, the direct part of the source value will be copied and a pointer to the (hidden and immutable) copy will be stored in the destination interface value.
In an assignment, if both the destination and source values are interface values, then (the direct part of) the dynamic value of the source interface value will be copied and the copy result will be stored in the destination interface value as dynamic value. The standard compiler makes some optimizations here so that a pointer is always copied in the process.
Go specification states:
So, the comparison rule is much like the assignment rule. In other words, two values are comparable if one can be implicitly converted to the type of the other.
The above rule doesn't cover all circumstances. What about if both of the two operands in a comparison are untyped constant values? The additional rules are simple:The results of comparing two untyped constant values obey intuition.
Any comparison results an untyped boolean value.
Note, currently (Go 1.11), if one of the two operands in a comparison is an interface value, and the other operand is a non-interface value of an uncomparable type which implements the former operand interface type, both the standard Go compiler (gc) and gccgo fail to compile the comparison. This might be a bug of gc and gccgo, or not. When this issue is clarfied and it is proved it is not a bug, the above comparison rule descriptions will get adjusted accordingly.
nil identifier).
nil identifiers (untyped nil values) can't be compared with each other.
Here are some uncomparable types.
[]int
map[string]bool
func() int
struct{x map[int]int} // one field element type is uncomparable
[5][]int // array element type is uncomparable
Comparison restriction examples:
package main
// The types of the following variables are all uncomparable.
var s []int
var m map[int]int
var f func()()
var t struct {
s []int
n 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
*/
// The following lines compile okay.
_ = s == nil
_ = m == nil
_ = f == nil
}
T
(if they have different types, one of them must be implicitly convertible to the type of the other).
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.
T is a boolean, numeric or pointer
(either safe or unsafe), then each pair of the corresponding bytes
occupied in memory by the two values will be compared.
The two values are equal only if all the byte comparisons result true.
T is a channel type, the two channel values are equal
if they both reference the same underlying channel structure.
Please note, comparing two interfaces with the same uncomparable dynamic type produces a panic. A sub-comparison of the first three cases mentioned above may also be an interface value comparison. So comparisons of the first three cases may be also possible to produce panics.
Here is an example in which some panics will occur in comparisons.
package main
func main() {
type T struct {
a interface{}
b int
}
var x interface{} = []int{}
var y = T{a: x}
var z = [3]T{}
// Each of the following line can produce a panic.
_ = x == x
_ = y == y
_ = z == z
}
Please note, the two code lines involved z compile okay for
the go build and go install commands in any version of Go SDK,
but fail to compile for the go run command in Go SDK 1.9 and 1.10.
This is a known bug of the standard Go compiler 1.9 and 1.10.
The Go 101 project is hosted on both github and gitlab. 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.
Support Go 101 by playing Tapir's games.