https://www.youtube.com/watch?v=WvLc4scMlAY

Identical types in Go

First case of assignability says:

If right’s type is identical to T then assignment is completely valid.

It may sound so obvious to not be even worth to mention but there are couple of subtle points when it goes to identity of types. A deep dive into this topic can also help out with understanding other related basic concepts from the language.

Type declaration

To create new type name there is a special statement using keyword type:

type A struct{ name string }
type B A
type (
C string
D map[string]int
)

Underlying type

Each type in Go has something which is called an underlying type. Universe block contains some predeclared identifiers binding to types like boolean, string or numeric. For each predeclared type T its underlaying type is T (no trap here). For type literals it’s exactly the same:

// Sample type literals
var (
a [10]int
b struct{ name string }
c *int
d func(p int) (r int)
e interface {
f(int) int
}
f []int
g map[string]int
h chan<- string
)
Type declarations can be “factored” into blocks to avoid repeating var keyword many times. The same method can be applied to type declarations as shown in the first code snippet.

In other cases underlying type of T is the underlying type of type bind through type declaration:

type X string  // underlying type of X is string
type Y X // underlying type of Y is string
type Z [10]int // underlying type of Z is [10]int

(un)named types

Named types are new ones specified using type name, optionally prefixed with package name. Package name is used to access name exported from other package (preceded with proper import statement):

package main
import “fmt”
type T fmt.Formatter // T and fmt.Formatter are named types

Qualified identifiers (the ones with package name prefix) cannot refer to the current package:

package foo
type A struct{ name string }
type B foo.A // compiler throws "undefined: foo in foo.A"

Unnamed types use type literals while referring to themselves like f.ex.:

  • map[string]int
  • chan<- int
  • []float32

Type identity

Having some basic concepts grasped it’s easy to understand when two types in Go are identical or different:

  1. two named types are identical if both are created through the same type declaration:
var (
T1 string
T2 string
)

T1 and T1 are identical. T1 and T2 are different as declared using two separate type declarations (even if factored into single block),

2. named and unnamed types are different (no exceptions),

3. unnamed types are identical if corresponding type literals are the same (details of type literals identity are clearly described in language spec).