【Go】型の変換を覗いてみる


Go初心者の筆者がGoの型周りでハマったところをピックアップしてまとめました。[^1]


## 変数の型
Goでは`var`キーワードを用いて変数に静的な型をつけることができます。

```go
var a int
a = 10      // int型に整数を代入
var x = 11  // 宣言時に代入も可能
```
`interface{}`を用いて動的な型を変数につけることもできます。
(ここでは割愛します。)

## 定数の型
先ほどの例において、`10`のような定数の型はどう定義されているのでしょうか。
「[The Go Programming Language Specification](https://golang.org/ref/spec#Constants)」では次のように説明されています。
>
The default type of an untyped constant is bool, rune, int, float64, complex128 or string respectively, depending on whether it is a boolean, rune, integer, floating-point, complex, or string constant.
>

ここで、型なし定数(untyped contstant)とは、明示的な型がついていない定数のことです。
定数自体に型はついていないが、変数などに代入される際に変数へ型をつけられるような定数のことです。

型なし定数のデフォルトの型(the default type of untyped constant)についても説明しておきます。
明示的な型のない変数(例えば`var x`と宣言した時の`x`)へ型なし定数を代入する場合に、変数につける型が必要となります。さもなければ、型のない値を持った型のない変数が誕生してしまいます。
そこで、そのような型付きの値が必要とされる場合に型なし定数につける型のことをデフォルトの型(default type)と呼びます。

型なし定数のデフォルトの型の対応は次の通りになります。

| ブール値 | ルーン | 整数 | 浮動小数 | 複素数     | 文字列 |
|----------|--------|------|----------|------------|--------|
| bool     | rune   | int  | float64  | complex128 | string |

例えば、`10`のデフォルトの型は`int`型です.

## 変数に定数を代入する
変数を宣言した時の型とは異なるデフォルトの型を持つ定数を代入した時の挙動はどうなるのでしょうか。
以下の例で見てみましょう。

```sample.go
package main
import (
  "fmt"
  "reflect"
)

func main() {
  var a float64
  a = 1
  fmt.Println(reflect.TypeOf(a));
}
```

`reflect`パッケージを利用することで変数の型をみることができます。
この例では、変数`a`は`float64`型として宣言されています。
そこに型なし定数である`1`を代入しています。

これを実行してみましょう。

```
$ go run sample.go
float64
```

定数のデフォルトである`int`型ではなく、変数に宣言した`float64`になっています。

型のある変数に型なし定数を代入する場合の動作について次のような[説明](https://golang.org/ref/spec#Variables)がありました。
>
A constant may be given a type explicitly by a constant declaration or conversion, or implicitly when used in a variable declaration or an assignment or as an operand in an expression. It is an error if the constant value cannot be represented as a value of the respective type.
>

要約すると、

* 定数の型は、
  * 明示的に型を指定できる
  * 変数宣言や代入、式のオペランドとして暗黙的に指定できる
* 定数の値ををそれぞれの型の値として表すことができない場合はエラーとなる

ということです。

この場合、変数に明示的な型がついているため、定数`1`の型は暗黙的に`float64`型と指定されています。
デフォルトの型は必要とされていないので、変数`a`が`int`型となることはありません。

## 変数の代入と演算子
変数に変数を代入する場合は型が一致していなければなりません。[^2]

演算子についても、変数の型は一致していなければなりませんが、**一方が型を持つ変数で、もう一方が型なしの定数の場合**はシフト演算子を除いて暗黙の型変換が発生します。
>
Except for shift operations, if one operand is an untyped constant and the other operand is not, the constant is implicitly converted to the type of the other operand.
>

例をみてみましょう。

```go
time.Sleep(50 * time.Millisecond)
```
演算子`*`について左側は型なし定数`50`(`int`型)、右側は静的な型のついた変数`time.Millisecond`(`time.Duration`型)となっています。
したがって、型なし定数`50`は`time.Duration`型に暗黙的に型変換されます。

さて、次の例を考えてみましょう。

```go
var interval = 50
time.Sleep(interval * time.Millisecond)
```

結論から言って、このコードはコンパイルできずに、エラーとなります。

順に見ていきましょう。
まず、変数`interval`に型なし定数`50`が代入されます。
この時、変数`interval`の型は型なし定数のデフォルトの型ある、`int`となります。

次に関数`time.Sleep`の引数に式`interval * time.Millisecond`を渡しています。
この引数の式に着目すると、変数`interval`の型と`time.Millisecond`の型が一致していないため、エラーとなることがわかります。

## 最後に
この辺りは慣れてくればば、ほとんど意識しなくても問題ないのですが、理解が曖昧だと後々つまづきます。
時間があれば動的な型付け等の利用方法についてまとめてみます。

## 参考
* [The Go Programming Language Specification](https://golang.org/ref/spec)
* [Go言語の型とreflect](https://qiita.com/atsaki/items/3554f5a0609c59a3e10d)
* [time.Millisecond * int confusion](https://stackoverflow.com/questions/19337955/time-millisecond-int-confusion)

[^1]: あくまで、「こんな仕様だったな」ということを思い出す目的で書いているので、ザックリとした説明になっています。
[^2]: 名前を持つ型や基礎型についてなど、細かい話があります