Goの代入可能性についてちゃんと理解してみる

# はじめに
今月でGolangに触れてからちょうど一年になりますが、未だにGoについて初めて知ることが多々あります。その一つを紹介したいと思います。

以下のGoでなぜキャストが必要なのでしょうか？　当たり前だろ、GoはCなどとは違って明示的なキャストが必要な言語だろとツッコミが入りそうですが、

```golang
type INT int
var i1 int
var i2 INT
i1 = 1
i2 = INT(i1)  // 明示的なキャストが必要
```
では、以下はキャスト不要で代入できますが、これはなぜでしょうか？　といわれると案外答えられない人もいるのではないでしょうか？
そのあたりを[Goの仕様](https://golang.org/ref/spec)をちゃんと読んで理解しましょうというのが今回のテーマです。

```golang
type INTSLICE []int
var l1 []int
var l2 INTSLICE
l1 = []int{1,2,3}
l2 = l1  // 明示的なキャストが不要
```

# 仕様をよんでみる
代入可能性(`Assignability`)について定義されているのは、https://golang.org/ref/spec#Assignability です。
`Assignability`があると関数の引数や返り値に指定できたり、比較演算値でお互いに比較が可能になったりします。他に何ができるようになるか全て知りたければGoの仕様を`assignable`でgrepしましょう。

`Assignability`の記述を引用します（連番は筆者による）。
> A value x is assignable to a variable of type T ("x is assignable to T") if one of the following conditions applies:
1. x's type is identical to T.
2. x's type V and T have identical underlying types and at least one of V or T is not a defined type.
3. T is an interface type and x implements T.
4. x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not a defined type.
5. x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type.
6. x is an untyped constant representable by a value of type T.

こちらをちゃんと理解して、`INT`のキャストが必要な理由を把握しましょう。
No.5などは確かにそういえばと思いますね。ポインタを返す関数でnilを返却できたりしますもんね。
No.1をよんで「型の同一性とは？」となりますが、それはすぐ上の

https://golang.org/ref/spec#Type_identity にあります。
抜粋すると、
> A defined type is always different from any other type. Otherwise, two types are identical if their underlying type literals are structurally equivalent; that is, they have the same literal structure and corresponding components have identical types. In detail:
・ Two array types are identical if they have identical element types and the same array length.
・ Two slice types are identical if they have identical element types.
・ Two struct types are identical if they have the same sequence of fields, and if corresponding fields have the same names, and identical types, and identical tags. Non-exported field names from different packages are always different.
...(省略)

`defined type`とか`underlying type`という用語がでてきます。ここではごく一部のみ引用し、説明しますが、正確な定義は仕様をご確認ください。

ざっくり説明すると、新しい型を定義するときの `type [defined type] [underlying type]`に該当します（後述の例の型`B`や`D`のように異なる場合があります。後ほど正確な定義に触れます）。
`type INT int`でいうところの`defined type`が`INT`、`underlying type`が`int`です。

`defined type`の仕様では、
> The new type is called a defined type. It is different from any other type, including the type it is created from.

とあり、`INT`と`int`は全く別物とみなされます。
少し脱線しますが、
> A defined type may have methods associated with it. It does not inherit any methods bound to the given type, but the method set of an interface type or of elements of a composite type remains unchanged:

とあるように、`type`ではメソッドは引き継がれません。

`underlying type`の仕様では、

> Each type T has an underlying type: If T is one of the predeclared boolean, numeric, or string types, or a type literal, the corresponding underlying type is T itself. Otherwise, T's underlying type is the underlying type of the type to which T refers in its type declaration.

とあります。`predeclared`とはGo言語側が事前に定義してある`int`や`string`などを指します。`type literal`とは
> TypeLit   = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
	    SliceType | MapType | ChannelType .

です。`[]int`などは`type literal`だとわかりますね！
一方、`int`、`string`などは`type literal`ではないことに注意してください。

`type declaration`（型宣言）は`type`による型定義の他、エイリアスによる型宣言の二つ両方を含みます。


注意深く読むと`underlying type`は再起的定義になっていて、

```
type A int
type B A

type C []B
type D C
```
の場合、`int`、`A`、`B`の`underlying type`は`int`であることや、`[]B`、`C`、`D`の`underlying type`は`[]B`であることが導かれます。

ここまで、「型の同一性」「defined type」「underlying type」といった概念が出てきました。これらを踏まえてやっと`Assignability`が理解できます。`Assignability`の説明に戻る前にここまででわかることを一度整理します。

* `INT`と`int`は型が異なる
* `INTSLICE`と`[]int`は型が異なる
* `INT`は`defined type`
* `INTSLICE`は`defined type`
* `int`の`underlying type`は`int`自身
* `INT`の`underlying type`は`int`
* `[]int`の`underlying type`は`[]int`自身
* `INTSLICE`の`underlying type`は`[]int`

では、`Assignability`の定義にもどりましょう。
まず、No.1は`INT`、`INTSLICE`の両方のケースにあてはまらないことがわかります。
No.2ですが、
> x's type V and T have identical underlying types and

この部分は両方のケースであてはまります。`INT`の`underlying type`は`int`、`INTSLICE`の`underlying type`は`[]int`だからです。

> at least one of V or T is not a defined type.

ここの条件から逆算すると、

* `int`は`defined type`
* `[]int`は`defined type`でない

。。。え、？`int`が`defined type`だと、、、、
この部分が今回の肝です。１０分くらい必死に仕様よんでカラクリに気づくまでかなり混乱しました。
が、わかってしまえば簡単。

**都合上、Go言語側で事前に`int`、`string`などは`type`で型定義しているのです**

https://golang.org/ref/spec#Numeric_types には以下にこう書いてます。
> To avoid portability issues all numeric types are defined types and thus distinct except byte, which is an alias for uint8, and rune, which is an alias for int32.

なるほどですね。ちなみに`byte`は`uint8`のエイリアス、`rune`は`int32`のエイリアスなので、それらは互いに代入可能です（キャストなしで代入できます）

試しに、Golandでおもむろに`int`に対して、「Go to Declartion」すると、
https://github.com/golang/go/blob/be08e10b3bc07f3a4e7b27f44d53d582e15fd6c7/src/builtin/builtin.go#L75
にて、`type int int`との定義があります。

これですっきりしたのでした！！

# おまけ
今日のテーマは代入可能性でしたが、キャストで型変換可能かはまた別の話で、 https://golang.org/ref/spec#Conversions にあります。

constant等はまた別に細かい定義があるようですが、constant以外では以下の記載があります。

> A non-constant value x can be converted to type T in any of these cases:
・x is assignable to T.
・ignoring struct tags (see below), x's type and T have identical underlying types.
・ignoring struct tags (see below), x's type and T are pointer types that are not defined types, and their pointer base types have identical underlying types.
・x's type and T are both integer or floating point types.
・x's type and T are both complex types.
・x is an integer or a slice of bytes or runes and T is a string type.
・x is a string and T is a slice of bytes or runes.

`x's type and T have identical underlying types`の部分の条件で、`INT`は`int`にキャスト可能になるんですね。
型の同一性判定では、`struct`の`tag`も一致する必要があったのですが、型変換可能性においては`tag`は無視するのが興味深いですね。

最後に今回の総復習として、コードを載せておきます。この挙動が理解できたらカンペキです！！

```golang
package main
import "fmt"

type point struct {
  x int
  y int
}

type point2 struct {
  x int
  y int
}

type point3 struct {
  x int `json:"x"`
  y int `json:"y"`
}

func main() {
  var p1 = struct {
      x int
      y int
  } {
      200,
      400,
  }

  var p2 = struct {
      x int
      y int
  } {
      200,
      400,
  }

  fmt.Println(p1==p2) //true t1とt2は型同一かつ値が同じ
  p1 = p2 //型同一なのでキャストなしで代入化

  var p3 point = p2
  var p4 point2 = p2

  fmt.Println(p2==p3) // true p2とp3は型同一
  // fmt.Println(p3==p4) p3とp4は共にdefined typeなので型が異なる。コメントアウト外すとコンパイルエラー
  fmt.Println(p4==point2(p3)) // true 型変換は可能
  // var p5 point3 = p2 tagが異なるのでp2とp5は型が異なる。コメントアウト外すとコンパイルエラー
  var p5 point3 = point3(p2)
  fmt.Println(p5 == point3(p2)) // tagが異なってもキャスト可能
}
```