型定義
type T Uと宣言することで、型Uと同じ性質を持つ別の型Tを定義することができます。例えば、type Celsius float64と定義したとき、Celsiusとfloat64は別の型ですが、明示的な変換をすることで同じ値を代入することができます。
var c Celsius = 36.5
var f float64 = float64(c) // OK
// var f float64 = c // コンパイルエラー型エイリアス
type T = Uで定義できる型エイリアスでは、型Tと型Uは同一型として扱うことができます。
type Count = int
var n int = 0
var c Count = n // 同一型なので明示的な型変換が不要基底型
各型には基底型があります。基底型とは、type T Uによる定義をたどったとき終端にある型のことです。
type ID int
type UserID ID // UserIDの基底型はint複合型の同一性
配列・スライス・構造体・ポインタ・関数・インターフェース・マップ・チャネルといった複合型では、種類と内部の型構成が一致しなければ同一型とはみなされません。配列では要素型に加えて長さも型の一部になり、構造体ではフィールドの名前・型・並びが全て一致している必要があります。
[]intと[]intは同一型[]intと[3]intは別の型type A intとtype B intは別の型struct{ x int }とstruct{ y int }は別の型Stack[T any]や[]Tのように型パラメータTを含む型は、具体的な型引数を与えてインスタンス化したあとに同一型かどうか決まる。例えば、型引数を与えた結果Stack[int]とStack[string]となれば別の型になります。
代入可能性
値vを型Tの変数に代入できるかは、型の関係をもとにコンパイル時に判定されます。
同一型どうしはそのまま代入可能です。
var a int = 1
var b int = a基底型が同一の型定義どうしは、明示的に変換することで代入可能です。
var c Celsius = 36.5
var f float64 = float64(c)nilはポインタ、スライス、マップ、チャネル、関数、インターフェースに代入可能です。
var ptr *int = nil
var slice []int = nil
var m map[string]int = nil
var ch chan int = nil
var fn func() = nil
var i any = nilインターフェース変数には、そのインターフェースを実装する非インターフェース型の値を代入可能です。
import (
"errors"
)
// 組み込みのerrorインターフェースを実装した非インターフェース型が返る
var err error = errors.New("something went wrong")表現可能性
定数の型変換と代入において、値が型Tの取りうる値の範囲に収まるかはコンパイル時に判定されます。
const limit = 300
var wide int = limit // OK: 300はintの範囲内
// var tiny int8 = limit // コンパイルエラー: 300はint8の範囲外型を持たない定数は、その値が代入先の型で正確に表現できる場合のみ変換が可能です。
const whole = 1.0
var n int = int(whole) // OK
const frac = 1.5
// var n int = int(frac) // NG: 1.5は整数として正確に表現できない
const letter = 'A'
var b byte = letter // OK: 'A'は65でbyteに表現できる
var one string = string(letter) // OK: runeを1文字のstringに変換
const text = "go"
var s string = text // OK
var bs []byte = []byte(text) // OK: stringをバイト列に変換
var first byte = text[0] // OK: 先頭バイト 'g' (103)
// var lone byte = byte(text) // NG: 文字列全体はbyte1個に表現できない型付き定数は宣言時点でその型に表現できる値だけが許されます。
const max int8 = 127 // OK
// const bad int8 = 300 // NG: 300はint8に表現できない変数の型変換でコンパイルが通るかは、型の互換性によって決まります。値が範囲がだったとしても、float64からintのようにどちらも数値型の場合はコンパイルエラーは発生しません。実行時に切り捨てやオーバーフローが発生します。stringからintのような変換は互換性がないためコンパイルエラーになります。
// 定数の場合はコンパイルエラー
// const x float64 = 1.5
// var y int = int(x)
// 変数の場合はコンパイル可能
var n float64 = 1.5
var m int = int(n) // 切り捨てられて1になる