Go/underlying type
表示
< Go
Goにおける underlying type(基底型)とは、Goの型システムにおける重要な概念の一つで、型が最終的にどのように表現されるかを示すものです。Goの型は、名前付き型(named type)と型リテラル(type literal)に大きく分けられますが、それぞれの型は必ず一つの underlying type を持ちます。
定義と規則
[編集]Goの仕様によると、各型の underlying type は以下のように定義されます。
- プリミティブ型 (basic type):
bool,string,int,int8,int16,int32,int64,uint,uint8,uint16,uint32,uint64,uintptr,float32,float64,complex64,complex128の underlying type は、その型自身です。 - 配列型 (array type): 配列要素型の underlying type を持ちます。例えば、
[3]intの underlying type はintではありません。[3]intの underlying type は[3]int自身です。 - スライス型 (slice type): 要素型の underlying type を持ちます。例えば、
[]intの underlying type は[]int自身です。 - 構造体型 (struct type): 構造体型の underlying type は、その構造体型自身です。
ポインタ型 (pointer type): ベース型の underlying type を持ちます。例えば、int の underlying type は *int 自身です。
- 関数型 (function type): 関数型の underlying type は、その関数型自身です。
- インターフェース型 (interface type): インターフェース型の underlying type は、そのインターフェース型自身です。
- マップ型 (map type): キー型と要素型の underlying type を持ちます。例えば、
map[string]intの underlying type はmap[string]int自身です。 - チャネル型 (channel type): 要素型の underlying type を持ちます。例えば、
chan intの underlying type はchan int自身です。 - 型名 (named type): 既存の型に名前をつけた型(
type MyInt intなど)の underlying type は、その元になった型の underlying type と同じになります。
具体例
[編集]package main import "fmt" type Celsius float64 type Fahrenheit float64 func main() { var c Celsius = 25.0 var f Fahrenheit = 77.0 var i int = 10 var s []int fmt.Printf("Type of c: %T, Underlying type of c: %T\n", c, underlyingType(c)) // Type of c: main.Celsius, Underlying type of c: float64 fmt.Printf("Type of f: %T, Underlying type of f: %T\n", f, underlyingType(f)) // Type of f: main.Fahrenheit, Underlying type of f: float64 fmt.Printf("Type of i: %T, Underlying type of i: %T\n", i, underlyingType(i)) // Type of i: int, Underlying type of i: int fmt.Printf("Type of s: %T, Underlying type of s: %T\n", s, underlyingType(s)) // Type of s: []int, Underlying type of s: []int fmt.Printf("Type of [3]int: %T, Underlying type of [3]int: %T\n", [3]int{}, underlyingType([3]int{})) // Type of [3]int: [3]int, Underlying type of [3]int: [3]int } // 型の underlying type をリフレクションを使って取得する関数 (あくまで説明用) func underlyingType(v interface{}) interface{} { return fmt.Sprintf("%T", v) // 実際には reflect.TypeOf(v).Kind() などを使う }
underlying type の重要性
[編集]underlying type は、Goの型システムの理解において非常に重要です。主に以下の点でその重要性が現れます。
- 型変換 (Type Conversion): 型変換が可能なのは、underlying type が同じであるか、特定のルールに従う場合に限られます。例えば、
CelsiusとFahrenheitは underlying type がfloat64なので、明示的な型変換が可能です。しかし、intとfloat64のように underlying type が異なる場合は、明示的な型変換が必要です。 - 比較 (Comparison): 比較演算子 (
==,!=,<,>,<=,>=) が適用できるのは、同じ underlying type を持つ型同士、または特定のインターフェース型と具象型の間など、Goの仕様で定められた条件を満たす場合に限られます。 - 型制約 (Type Constraints) (Go 1.18以降): ジェネリクスにおける型制約では、型パラメータが満たすべき型の集合を定義しますが、その際に underlying type が重要な役割を果たすことがあります。(Go 1.18 - Go 1.24 における core type の概念とも関連していました。)
- 型同一性: 2つの型が同じであるかどうかを判断する際に、名前だけでなく underlying type も考慮されます。
まとめ
[編集]underlying type は、Goの型がどのように内部的に表現され、どのように相互に変換・比較できるかを理解するための基礎となる概念です。名前付き型は、コードの可読性や意味合いを高めるために便利ですが、型の本質的な性質は underlying type によって決定されると言えます。