Go初心者の筆者がGoの型周りでハマったところをピックアップしてまとめました。1
変数の型
Goではvar
キーワードを用いて変数に静的な型をつけることができます。
var a int
a = 10 // int型に整数を代入
var x = 11 // 宣言時に代入も可能
interface{}
を用いて動的な型を変数につけることもできます。
(ここでは割愛します。)
定数の型
先ほどの例において、10
のような定数の型はどう定義されているのでしょうか。
「The Go Programming Language Specification」では次のように説明されています。
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
型です.
変数に定数を代入する
変数を宣言した時の型とは異なるデフォルトの型を持つ定数を代入した時の挙動はどうなるのでしょうか。
以下の例で見てみましょう。
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
になっています。
型のある変数に型なし定数を代入する場合の動作について次のような説明がありました。
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.
例をみてみましょう。
time.Sleep(50 * time.Millisecond)
演算子*
について左側は型なし定数50
(int
型)、右側は静的な型のついた変数time.Millisecond
(time.Duration
型)となっています。
したがって、型なし定数50
はtime.Duration
型に暗黙的に型変換されます。
さて、次の例を考えてみましょう。
var interval = 50
time.Sleep(interval * time.Millisecond)
結論から言って、このコードはコンパイルできずに、エラーとなります。
順に見ていきましょう。
まず、変数interval
に型なし定数50
が代入されます。
この時、変数interval
の型は型なし定数のデフォルトの型ある、int
となります。
次に関数time.Sleep
の引数に式interval * time.Millisecond
を渡しています。
この引数の式に着目すると、変数interval
の型とtime.Millisecond
の型が一致していないため、エラーとなることがわかります。
最後に
この辺りは慣れてくればば、ほとんど意識しなくても問題ないのですが、理解が曖昧だと後々つまづきます。
時間があれば動的な型付け等の利用方法についてまとめてみます。