型パラメータと型制約

型パラメータは将来の型引数のプレースホルダーであり、_型変数_のように動作します。その値はコンパイル時に分かっており、名前付き定数が数値、文字列、またはbool値を表すのと似ています。通常の変数と同様、型パラメータには型があります。その型は_型制約_によって記述され、型制約は各型パラメータの型に対してどのような操作が許可されるかを決定します。

型パラメータをインスタンス化する具体的な型は、その型パラメータの制約を満たす必要があります。これにより、型パラメータの型を持つ操作数が、型パラメータをインスタンス化するために使用される具体的な型が何であっても、各型制約のすべてのプロパティを持つことが保証されます。

Goでは、型制約はメソッド要件と型要件の混合により記述され、これらが一緒になって_型セット_を定義します。これは、すべての要件を満たすすべての型のセットです。Goはこの目的のために、インターフェースの一般化された形式を使用します。インターフェースは一連のメソッドと型を列挙し、このようなインターフェースによって記述される型セットは、それらのメソッドを実装し、列挙された型に含まれるすべての型で構成されます。

たとえば、以下のインターフェースによって記述される型セット:

type Constraint interface {
    ~[]byte | ~string
    Hash() uint64
}

は、表現が[]byteまたはstringで、メソッドセットにHashメソッドを含むすべての型で構成されます。

これにより、ジェネリック操作数に対する操作を制御するルールを記述できます。たとえば、インデックス式のルールは、型パラメータ型Pの操作数aについて(他の条件の中で)以下のように述べています:

インデックス式a[x]は、Pの型セット内のすべての型の値に対して有効でなければなりません。Pの型セット内のすべての型の要素型は同一でなければなりません。(この文脈では、string型の要素型はbyteです。)

これらのルールにより、以下のジェネリック変数sをインデックスすることが可能になります(playground):

func at[bytestring Constraint](s bytestring, i int) byte {
    return s[i]
}

インデックス操作s[i]が許可されるのは、sの型がbytestringであり、bytestringの型制約(型セット)に[]bytestring型が含まれており、これらに対してiによるインデックスが有効だからです。