Gold, an experimental Go local docs server, Go docs generation tool, and code reader. NEW!

-- show type implemention relations --

-- show code statistics --

-- smooth code view experiences --

-- and more... --

Value Conversion, Assignment and Comparison Rules in Go

This article will list all the value comparison, conversion and comparison rules in Go.

Value Conversion Rules

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 .

1. the apparent conversion rule

If two types denote the identical type, then their values can be implicitly converted to either type of the two.

values of type byte and uint8 can be converted to each other.

and can be converted to each other. values of type rune and int32 can be converted to each other.

and can be converted to each other. values of type []byte and []uint8 can be converted to each other. For example,

Nothing more to explain about this rule, whether you think this case involves conversions or not.

2. underlying type related conversion rules

x and a non-interface type T , assume the type of x is Tx , if Tx and T share the same underlying type (ignoring struct tags), then x can be explicitly converted to T .

and share the same underlying type (ignoring struct tags), then can be explicitly converted to . if either Tx or T is a non-defined type and their underlying types are identical (considering struct tags), then x can be implicitly converted to T .

or is a non-defined type and their underlying types are identical (considering struct tags), then can be converted to . if 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 . Given a non-interface valueand a non-interface type, assume the type ofis

(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 implicit conversions both doesn't work. /* is = ms // error ms = is // error */ // 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 } An example: 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 non-defined, 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 } Pointer related conversion example:

3. channel specific conversion rule

Given a channel value 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 a non-defined type, then x can be implicitly converted to T .

package main func main() { type C chan string type C1 chan Example:

4. interface implementation related conversion rules

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 a copy of x , if Tx is a non-interface type;

, if is a non-interface type; a copy of the dynamic value of x , if Tx is an interface type. Given a valueand an interface type, if the type (or the default type) ofisandimplements, thencan beconverted to type. The conversion result is an interface value (of type), which boxes

Given an interface value x with its dynamic type as T , x can be safely converted to type T through the type assertion syntax x.(T) .

Given an interface value x and an interface type I , if the dynamic type of x implements I , then x can be safely converted to I through the type assertion syntax x.(I) .

Please read interfaces in Go for details and examples.

5. untyped value conversion rule

An untyped value can be implicitly converted to type 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 } Example:

6. constants conversion rule

(This rule is some overlapped with the last one.)

Generally, converting a constant still yields a constant as result. (Except converting a constant string to byte slice or rune slice described in the below 8th rules.)

Given a constant value x and a 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() { 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) // must be explicitly const C1, C2 complex64 = F, I const I5 = int(C2) // must be explicitly } Example:

7. non-constant number conversion rules

Non-constant floating-point and integer values can be explicitly converted to any floating-point and integer types.

Non-constant complex values can be explicitly converted to any complex types.

Complex non-constant values can't be converted to floating-point and integer types.

Floating-point and integer non-constant values can't be converted to complex types.

Data overflow and rounding are allowed in non-constant number conversions. When converting a floating-point non-constant number to an integer, the fraction is discarded (truncation towards zero). An example: 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) } Note,An example:

8. string related conversion rules

If the type (or default type) of a value is an integer type, then the value can be explicitly converted to string types.

A string value can be explicitly converted to a slice type whose underlying type is []byte (a.k.a., []uint8 ), and vice versa.

A string value can be explicitly converted to a slice type whose underlying type is []rune (a.k.a., []int32 ), and vice versa.

Please read strings in Go for details and examples.

9. unsafe pointers related conversion rules

A pointer value of any type can be explicitly converted to a type whose underlying type is unsafe.Pointer , and vice versa.

An uintptr value can be explicitly converted to a type whose underlying type is unsafe.Pointer , and vice versa.

Please read type-unsafe pointers in Go for details and examples.

Value Assignment Rules

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.

Value Comparison Rules

Go specification states: In any comparison, the first operand must be assignable to the type of the second operand, or vice versa. 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 an exception for the above basic comparison rule. If one of the two operands in a comparison is an interface value, and the other operand is a non-interface value of an incomparable type (which should implement the former operand interface type), then the comparison is illegal, even if the non-interface value can be implicitly converted to the 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 above described basic rules don't cover all cases. What about if both of the two operands in a comparison are untyped (constant) values? The additional rules are simple: untyped boolean values can be compared with untyped boolean values.

untyped numeric values can be compared with untyped numeric values.

untyped string values can be compared with untyped string values. The above described basic rules don't cover all cases. 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 numeric values obey intuition. Note, an untyped nil value can't be compared with another untyped nil value. Any comparison results 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) } The following example shows some incomparable types related comparisons.

How Are Two Values Compared?

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.) If T is a boolean type, then the two values are equal only if they are both true or both false . If T is an integer type, then the two values are equal only if they have the same representation in memory. If T is a floating-point type, then the two values are equal only if any of the following conditions is satisfied: they are both +Inf .

. they are both -Inf .

. each of them is either -0.0 or +0.0 .

or . they are both not NaN and they have the same bytes representations in memory. If 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. If 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. If 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. If T is a struct type, then each pair of the corresponding fields of the two struct values will be compared. If T is an array type, then each pair of the corresponding elements of the two array values will be compared. If T is an interface type, please read how two interface values are compared. If T is a string type, please read how two string values are compared. Please note, comparing two interfaces with the same incomparable dynamic type produces a panic. The following 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{{a: y}} // Each of the following line can produce a panic. _ = x == x _ = y == y _ = z == z } Assume two values are comparable, and they have the same type. (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.)Please note, comparing two interfaces with the same incomparable dynamic type produces a panic. The following is an example in which some panics will occur in comparisons.