Go/型

出典: フリー教科書『ウィキブックス(Wikibooks)』
< Go


[編集]

型は、値の集合と、それらの値に固有の操作やメソッドを決定します。型がジェネリック(generic)である場合は、型名に続いて型引数(type arguments)を指定しなければなりません(Go1.18以降)。型は、型名がある場合はそれによって示され、既存の型から型を構成する型リテラルを使って指定することができます[1]

構文
Type      = TypeName [ TypeArgs ] | TypeLit | "(" Type ")" ;
TypeName  = identifier | QualifiedIdent ;
TypeArgs  = "[" TypeList [ "," ] "]" ;
TypeList  = Type { "," Type } ;
TypeLit   = ArrayType | StructType | PointerType | FunctionType | InterfaceType | SliceType | MapType | ChannelType ;

Goでは、特定の型名をあらかじめ宣言しています。その他の型は、型宣言によって導入されます。複合型(array、struct、pointer、function、interface、slice、map、channelの各型)は、型リテラルを使って構築することができます。

あらかじめ宣言された型、定義された型、型パラメータを名前付き型と呼びます。

エイリアス宣言で指定された型が名前付き型である場合、エイリアスは名前付き型を表します。

メソッドセット[編集]

ある型のメソッドセットは、その型のオペランドに対して呼び出すことのできるメソッドを決定します。すべての型は、それに関連する(おそらく空の)メソッドセットを持っています[2]

  • 定義された型Tのメソッドセットは、レシーバ型Tで宣言されたすべてのメソッドで構成されます。
  • 定義された型Tへのポインタ(Tはポインタでもインターフェースでもない)のメソッドセットは、レシーバ *TまたはTで宣言されたすべてのメソッドのセットです。

インターフェース型のメソッドセットは、インターフェースの型セット内の各型のメソッドセットの総和(intersection)です(結果のメソッドセットは通常、インターフェースで宣言されたメソッドのセットだけです)。

  • 埋め込みフィールドを含む構造体(および構造体へのポインタ)には、構造体型の項で説明したように、さらなるルールが適用されます。それ以外の型は空のメソッドセットを持ちます。

メソッドセットでは、各メソッドは空白でない一意のメソッド名を持っている必要があります。

論理型[編集]

論理型(boolean type)は、組込み型 bool です。

数値型[編集]

数値型(numeric type)は、整数または浮動小数点の値の集合を表します。

文字列型[編集]

文字列型(string type) は、組込み型 string です。

配列型[編集]

配列(Array)とは、要素の種類と呼ばれる単一の型の要素の番号付けされた列のことです。要素の数は配列の長さと呼ばれる非負の整数です[3]

構文
ArrayType   = "[" ArrayLength "]" ElementType ;
ArrayLength = Expression ;
ElementType = Type ;

長さは、配列の型の一部であり、int型の値で表現可能な非負の定数として評価されなければなりません。 配列aの長さは、組込み関数lenを使って知ることができます。 要素は、0からlen(a)-1までの整数の添字で指定できます。配列の型は常に1次元ですが、合成して多次元の型にすることもできます。

[32]byte
[2*N] struct { x, y int32 }
[1000]*float64
[3][5]int
[2][2][2]float64  // [2]([2]([2]float64))に同じ

スライス型[編集]

スライス(A slice)は、基礎となる配列の連続したセグメント(a contiguous segment)の記述子(descriptor)であり、その配列の要素の番号付けされたシーケンスへのアクセスを提供します[4]。 スライス型は、その要素タイプの配列のすべてのスライスの集合を表します。 要素の数はスライスの長さと呼ばれ、非負の整数です。 初期化されていないスライスの値はnilです。

構文
SliceType = "[" "]" ElementType ;

スライスsの長さは、組込み関数lenによって知ることができますが、配列とは異なり、実行中に変化する可能性があります。要素は、0 から len(s)-1 までの整数のインデックスで指定できます。ある要素のスライスインデックスは、基礎となる配列の同じ要素のインデックスよりも小さいかもしれません。

スライスは、一度初期化されると、その要素を保持する基礎となる配列と常に関連付けられます。スライスは、その配列や同じ配列の他のスライスとストレージを共有します。

スライスの基礎となる配列は、スライスの端を越えて延びることがあります。容量とは、スライスの長さとスライスを超えるアレイの長さの合計です。スライスaの容量は、組込み関数cap(a)を使って求めることができます。

与えられた要素タイプTに対する新しい初期化されたスライス値は、組込み関数makeを使って作られます。makeは、スライスタイプと、長さとオプションで容量を指定する仮引数を取ります。makeで作成されたスライスは、常に、返されたスライス値が参照する新しい隠れた配列を割り当てます。つまり、以下を実行すると

make([]T, length, capacity)

は、配列を確保してそれをスライスするのと同じスライスを生成するので、この2つの表現は等価です。

make([]int, 50, 100)
new([100]int)[0:50]

配列と同様、スライスは常に一次元ですが、組み合わせて高次元のオブジェクトを構築することができます。配列の配列では、構造上、内部の配列は常に同じ長さですが、スライスのスライス(またはスライスの配列)では、内部の長さは動的に変化する可能性があります。さらに、内部のスライスは個別に初期化する必要があります。

構造体型[編集]

構造体(A struct)は、フィールド(fields)と呼ばれる名前の付いた要素の並びで、それぞれが名前と型を持っています[5]。 フィールド名は、明示的に(IdentifierList)または暗黙的に(EmbeddedField)指定できます。構造体の中では、空白でないフィールド名は一意でなければなりません。

構文
StructType    = "struct" "{" { FieldDecl ";" } "}" ;
FieldDecl     = (IdentifierList Type | EmbeddedField) [ Tag ] ;
EmbeddedField = [ "*" ] TypeName ;
Tag           = string_lit ;
// 空の構造体です。
struct {}

// A struct with 6 fields.
struct {
	x, y int
	u float32
	_ float32  // 詰め物
	A *[]int
	F func()
}

型で宣言されているが、明示的なフィールド名を持たないフィールドを埋込みフィールド(An embedded field)と呼びます。 埋込みフィールドは、型名Tまたは非インタフェース型名*Tへのポインターとして指定されなければならず、T自体はポインター型であってはならない。 非修飾の型名はフィールド名として機能します。

// T1型、*T2型、P.T3型、*P.T4型の4つの埋め込みフィールドを持つ構造体です。
struct {
	T1        // field name is T1
	*T2       // field name is T2
	P.T3      // field name is T3
	*P.T4     // field name is T4
	x, y int  // field names are x and y 
}

フィールド名は構造体型の中で一意でなければならないため、以下の宣言は違法です。

struct {
	T     // 埋め込みフィールドと競合する *T および *P.T
	*T    // 埋め込みフィールド T および *P.T と競合します。
	*P.T  // 埋め込みフィールド T および *T と競合します。
}

A field or method f of an embedded field in a struct x is called promoted if x.f is a legal selector that denotes that field or method f.


x.f がそのフィールドまたはメソッド f を示す合法的なセレクタである場合、構造体 x の埋め込みフィールドまたはメソッド f はプロモートされたと呼ばれます。

Promoted fields act like ordinary fields of a struct except that they cannot be used as field names in composite literals of the struct.


プロモートされたフィールドは、構造体の複合リテラルでフィールド名として使用できないことを除いて、構造体の通常のフィールドのように動作します。

構造体の型Sと定義された型Tが与えられたとき、以下のようにプロモートされたメソッドが構造体のメソッドセットに含まれます。

  • Sが埋め込みフィールドTを含む場合、Sと*Sのメソッドセットには、レシーバTを持つプロモートされたメソッドが両方とも含まれます。
  • Sが埋め込みフィールド*Tを含んでいる場合、Sと*Sのメソッドセットは両方ともレシーバTまたは*Tを持つプロモートされたメソッドを含んでいます。

フィールド宣言の後には、オプションで文字列リテラルのタグを付けることができます。 このタグは、対応するフィールド宣言のすべてのフィールドの属性となります。空のタグ文字列は、タグがないのと同じです。 タグは、リフレクション・インターフェースを通じて表示され、構造体の型の識別に関与しますが、それ以外は無視されます。

struct {
	x, y float64 "" // 空のタグ文字列は、タグがないのと同じです。
	name string "どんな文字列でもタグとして許される"
	_ [4]byte "ceci n'est pas un champ de structure" //こいつは構造体じゃねぇ(仏)
}

// TimeStampプロトコルバッファに対応する構造体です。
// タグ文字列は、プロトコルバッファのフィールド番号を定義します。
// タグ文字列はプロトコルバッファのフィールド番号を定義します。
struct {
	microsec  uint64 `protobuf:"1"`
	serverIP6 uint64 `protobuf:"2"`
}


ポインター型[編集]

ポインター型(A pointer type)は、ポインターの基本型と呼ばれる所定の型の変数へのすべてのポインターの集合を表します。初期化されていないポインターの値はnilです[6]

構文
PointerType = "*" BaseType ;
BaseType    = Type ;
*Point
*[4]int

関数型[編集]

関数型(A function type)とは、仮引数と結果の型が同じであるすべての関数の集合を表します。初期化されていない関数型の変数の値はnilです[7]

構文
FunctionType   = "func" Signature ;
Signature      = Parameters [ Result ] ;
Result         = Parameters | Type ;
Parameters     = "(" [ ParameterList [ "," ] ] ")" ;
ParameterList  = ParameterDecl { "," ParameterDecl } ;
ParameterDecl  = [ IdentifierList ] [ "..." ] Type ;

仮引数または結果のリストの中で、名前(IdentifierList)はすべて存在するか、すべて存在しないかのいずれかでなければならない。 存在する場合、各名前は指定されたタイプの1つのアイテム(仮引数または結果)を表し、署名内の空白でない名前はすべて一意でなければなりません。 存在しない場合、各タイプはそのタイプの1つのアイテムを表します。仮引数と結果のリストは常に括弧で囲まれています。 ただし、名前のない結果が1つだけある場合は、括弧で囲まれていない型として記述することができます。

関数のシグネチャの最後の入力仮引数には、... の接頭辞が付いた型を持つことができます。このような仮引数を持つ関数は variadic と呼ばれ、その仮引数に0個以上の引数を付けて起動することができます。

func()
func(x int) int
func(a, _ int, z float32) bool
func(a, b int, z float32) (bool)
func(prefix string, values ...int)
func(a, b int, z float64, opt ...interface{}) (success bool)
func(int, int, float64) (float64, *[]int)
func(n int) func(p *T)

インターフェース型[編集]

インターフェース型(An interface type)は型集合を定義します[8]。 インターフェース型の変数は、インターフェースの型集合に含まれる任意の型の値を格納することができます。このような型はインタフェースを実装している(implement the interface)と言われます。初期化されていないインターフェース型の変数の値はnilです。

構文
InterfaceType  = "interface" "{" { InterfaceElem ";" } "}" .
InterfaceElem  = MethodElem | TypeElem .
MethodElem     = MethodName Signature .
MethodName     = identifier .
TypeElem       = TypeTerm { "|" TypeTerm } .
TypeTerm       = Type | UnderlyingType .
UnderlyingType = "~" Type .

インターフェイス型(interface type)は、インターフェース要素(interface elements)のリストで指定されます。 インターフェース要素はメソッドか型要素(type element)で、型要素は1つ以上の型項目(type term)の組合わせ(union)です。 型項は単一の型または単一の基礎となる型(underlying type)です。 Go1.18からインターフェース要素にメソッド以外に型要素が許されるようになりました。

基本インターフェース[編集]

最も基本的な形として、インターフェースはメソッドのリスト(空かもしれ ない)を指定します。 このようなインターフェイスで定義される型集合は、これらのメソッドをすべて実装する型の集合で、対応するメソッド集合はインターフェイスで指定されたメソッドのみで構成されます。 型集合が完全にメソッドのリストで定義できるインターフェイスは基本インターフェイス(Basic interfaces)と呼ばれます[9]。 Go1.17まではインターフェースと言えばGo1.18以降の基本インターフェースのことでした。

// シンプルなFileインターフェース
interface {
	Read([]byte) (int, error)
	Write([]byte) (int, error)
	Close() error
}

明示的に指定された各メソッドの名前は一意でなければならず、空白であってはなりません。

interface {
	String() string
	String() string  // 違法:String が一意でない
	_(x int)         // 違法:メソッドは空白でない名前でなければならない
}

1つのインターフェイスを複数のタイプが実装することができます。例えば、2つのタイプS1とS2が、メソッドセット

func (p T) Read(p []byte) (n int, err error)
func (p T) Write(p []byte) (n int, err error)
func (p T) Close() error

(TはS1またはS2のいずれかを表す)その場合、S1とS2がどのような他のメソッドを持っているか、または共有しているかにかかわらず、FileインターフェースはS1とS2の両方で実装されます。

型は、そのメソッドの任意のサブセットからなる任意のインターフェースを実装し、したがって、いくつかの異なるインターフェースを実装することができる。例えば、すべての型は空のインターフェースを実装します。

interface{}

事前に宣言された型anyは空のインターフェースのエイリアスです。

同様に、型宣言の中に登場する、Lockerというインターフェイスを定義するインターフェイス仕様を考えてみましょう。

type Locker interface {
	Lock()
	Unlock()
}

S1とS2も実装すれば

func (p T) Lock() {  }
func (p T) Unlock() {  }

これらのインターフェイスは、Fileインターフェイスと同様にLockerインターフェイスを実装しています。

インターフェイスTは、メソッドの指定の代わりに、(修飾されている場合もある)インターフェイス型名Eを使用することができます。Tのメソッドセットは、Tの明示的に宣言されたメソッドのメソッドセットと、Tの埋め込まれたインターフェイスのメソッドセットの組合わせ(union)である。

type Reader interface {
	Read(p []byte) (n int, err error)
	Close() error
}

type Writer interface {
	Write(p []byte) (n int, err error)
	Close() error
}

// ReadWriterのメソッドは、Read、Write、Closeの3つです
type ReadWriter interface {
	Reader  // ReadWriterのメソッドセットにReaderのメソッドを含む
	Writer  // ReadWriterのメソッドセットの中にWriterのメソッドを含む
}

メソッドセットの組合わせには、各メソッドセットの(エクスポートされた、およびエクスポートされない)メソッドが一度だけ含まれており、同じ名前のメソッドは同一のシグネチャを持つ必要があります。

type ReadCloser interface {
	Reader   // ReadCloserのメソッドセットにReaderのメソッドを含む
	Close()  // 不正:Reader.CloseとCloseのシグネチャは異なる
}

インターフェイス型Tは、自分自身やTを埋め込むインターフェイス型を再帰的に埋め込むことはできません。

// 違法です。Badは自分自身を埋め込むことはできない
type Bad interface {
	Bad
}

// 違法です。Bad1はBad2を使って自分自身を埋め込むことはできない
type Bad1 interface {
	Bad2
}
type Bad2 interface {
	Bad1
}


マップ型[編集]

マップは、要素タイプと呼ばれる1つのタイプの要素を、キータイプと呼ばれる別のタイプのユニークなキーの集合でインデックス化した順不同のグループです。初期化されていないマップの値はnilです[10]

構文
MapType     = "map" "[" KeyType "]" ElementType ;
KeyType     = Type ;

比較演算子 == と != は、key 型のオペランドに対して完全に定義されていなければなりません。 つまり、key 型は、function、map、slice であってはなりません。キー・タイプがインターフェース・タイプの場合、これらの比較演算子はダイナミック・キーの値に対して定義されていなければなりません。

map[string]int
map[*T]struct{ x, y float64 }
map[string]interface{}

マップの要素数は長さと呼ばれます。マップmの長さは、組込み関数lenで知ることができ、実行中に変化する可能性があります。 マップの要素は、実行中に代入で追加したり、インデックス式で取得したり、組込み関数deleteで削除することができます。

新しい空のマップ値は、マップタイプとオプションの容量ヒントを引数に取る組込み関数makeを使って作られます。

make(map[string]int)
make(map[string]int, 100)

初期容量はそのサイズを制限するものではありません。 マップは、nilマップ(A nil map)を除いて、格納されたアイテムの数に応じて成長します。 nilマップは、要素が追加されないことを除けば、空のマップ(an empty map)と同じです。

チャネル型[編集]

チャネルは、同時に実行される関数が、指定された要素タイプの値を送受信することで通信するためのメカニズムを提供します。初期化されていないチャンネルの値はnilです[11]

構文
ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType ;

オプションの <- 演算子で、チャネルの方向(送信または受信)を指定します。 方向が指定された場合、チャネルは方向性を持ちますが、そうでない場合は双方向性となります. チャネルは、代入や明示的な変換によって、送信のみ、または、受信のみの制約を受けることがあります。

chan T          // T型の値を送受信するために使用可能
chan<- float64  // float64 の送信にのみ使用できます。
<-chan int      // int の受信にのみ使用可能

<-演算子は、可能な限り左端のチャンネルに関連付けます。

chan<- chan int    // chan<- (chan int) に同じ
chan<- <-chan int  // chan<- (<-chan int) に同じ
<-chan <-chan int  // <-chan (<-chan int) に同じ
chan (<-chan int)

初期化された新しいチャネルの値は、チャネルタイプとオプションの容量を引数に取る組込み関数makeを使って作ることができます。

make(chan int, 100)

容量は、チャネルのバッファのサイズを要素数で設定します。 容量がゼロまたは存在しない場合、チャネルはバッファリングされておらず、送信者と受信者の両方が準備できている場合にのみ通信が成功します。 それ以外の場合は、チャネルはバッファリングされており、バッファが満杯でない場合(送信)、または空でない場合(受信)、通信はブロックされることなく成功します。 nilのチャネルは決して通信の準備ができていません。

チャネルは組込み関数closeで閉じることができます。受信演算子の多値割り当て形式では、チャネルが閉じられる前に受信値が送信されたかどうかが報告されます。

1つのチャネルは、送信文、受信操作、組込み関数caplenの呼び出しに使用することができ、さらに同期をとることなく、いくつものゴルーチン(goroutine)で使用することができます。 チャンネルは先入れ先出しのキューとして機能します。 例えば、1つのゴルーチンがチャネルで値を送信し、2つ目のゴルーチンがそれを受信する場合、値は送信された順に受信されます。

脚註[編集]

  1. ^ “Types¶”. The Go Programming Language Specification. The Go website. (March 10, 2022). https://golang.org/ref/spec#Types. 
  2. ^ “Method sets¶”. The Go Programming Language Specification. The Go website. (March 10, 2022). https://golang.org/ref/spec#Method_sets. 
  3. ^ “Array types¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Array_types. 
  4. ^ “Slice types¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Slice_types. 
  5. ^ “Struct_types¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Struct_types. 
  6. ^ “Pointer types¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Pointer_types. 
  7. ^ “Function types¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021). https://golang.org/ref/spec#Function_types. 
  8. ^ “Interface types¶”. The Go Programming Language Specification. The Go website. (March 10, 2022). https://golang.org/ref/spec#Interface_types. 
  9. ^ “Basic interfaces¶”. The Go Programming Language Specification. The Go website. (March 10, 2022). https://go.dev/ref/spec#Basic_interfaces. 
  10. ^ “Map types¶”. The Go Programming Language Specification. The Go website. (March 10, 2022). https://golang.org/ref/spec#Map_types. 
  11. ^ “Channel types¶”. The Go Programming Language Specification. The Go website. (March 10, 2022). https://golang.org/ref/spec#Channel_types.