Goプログラミング言語仕様

言語バージョン go1.25 (2025年2月25日)

はじめに

これはGoプログラミング言語のリファレンスマニュアルです。詳細情報やその他のドキュメントについては、go.devを参照してください。

Goは、システムプログラミングを念頭に置いて設計された汎用言語です。強力な型付けとガベージコレクションを備え、並行プログラミングを明示的にサポートしています。プログラムはパッケージから構成され、そのプロパティにより依存関係の効率的な管理が可能になります。

構文はコンパクトで解析が容易であり、統合開発環境などの自動ツールによる簡単な分析を可能にします。

表記法

構文は拡張バッカス・ナウア記法 (EBNF) のバリアントを使用して指定されています。

Syntax      = { Production } .
Production  = production_name "=" [ Expression ] "." .
Expression  = Term { "|" Term } .
Term        = Factor { Factor } .
Factor      = production_name | token [ "…" token ] | Group | Option | Repetition .
Group       = "(" Expression ")" .
Option      = "[" Expression "]" .
Repetition  = "{" Expression "}" .

プロダクションは、項と以下の演算子から構成される式であり、優先順位は昇順です。

|   alternation
()  grouping
[]  option (0 or 1 times)
{}  repetition (0 to n times)

小文字のプロダクション名は、字句 (終端) トークンを識別するために使用されます。非終端はCamelCaseで表記されます。字句トークンは二重引用符""またはバッククォート``で囲まれます。

a … bの形式は、aからbまでの文字の集合を代替として表します。横の省略記号は、仕様の他の箇所でも、さらに指定されていない様々な列挙やコードスニペットを非公式に表すために使用されます。文字(3文字の...とは異なります)は、Go言語のトークンではありません。

[Go 1.xx]の形式のリンクは、記述されている言語機能(またはその一部)が言語バージョン1.xxで変更または追加されたため、ビルドには最低限その言語バージョンが必要であることを示します。詳細は、付録リンクされたセクションを参照してください。

ソースコードの表現

ソースコードはUTF-8でエンコードされたUnicodeテキストです。テキストは正規化されていないため、単一のアクセント付きコードポイントは、アクセントと文字を組み合わせた同じ文字とは区別されます。これらは2つのコードポイントとして扱われます。簡潔にするため、このドキュメントでは、ソーステキスト中のUnicodeコードポイントを指すのに、修飾なしの用語文字を使用します。

各コードポイントは区別されます。例えば、大文字と小文字は異なる文字です。

実装上の制約:他のツールとの互換性のため、コンパイラはソーステキスト中のNUL文字(U+0000)を禁止する場合があります。

実装上の制約:他のツールとの互換性のため、コンパイラはソーステキストの最初のUnicodeコードポイントがUTF-8エンコードされたバイトオーダーマーク(U+FEFF)である場合、それを無視する場合があります。バイトオーダーマークはソースの他の場所では禁止される場合があります。

文字

以下の用語は、特定のUnicode文字カテゴリを示すために使用されます。

newline        = /* the Unicode code point U+000A */ .
unicode_char   = /* an arbitrary Unicode code point except newline */ .
unicode_letter = /* a Unicode code point categorized as "Letter" */ .
unicode_digit  = /* a Unicode code point categorized as "Number, decimal digit" */ .

Unicode Standard 8.0のセクション4.5「一般カテゴリ」は、文字カテゴリのセットを定義しています。Goは、文字カテゴリLu、Ll、Lt、Lm、Loのいずれかの文字をUnicodeの文字として扱い、NumberカテゴリNdの文字をUnicodeの数字として扱います。

文字と数字

アンダースコア文字_ (U+005F) は小文字として扱われます。

letter        = unicode_letter | "_" .
decimal_digit = "0" … "9" .
binary_digit  = "0" | "1" .
octal_digit   = "0" … "7" .
hex_digit     = "0" … "9" | "A" … "F" | "a" … "f" .

字句要素

コメント

コメントはプログラムのドキュメントとして機能します。形式は2種類あります。

  1. 行コメントは文字シーケンス//で始まり、行末で終わります。
  2. 一般コメントは文字シーケンス/*で始まり、最初の後続の文字シーケンス*/で終わります。

コメントはルーンまたは文字列リテラル内、あるいはコメント内では開始できません。改行を含まない一般コメントはスペースのように機能します。その他のコメントは改行のように機能します。

トークン

トークンはGo言語の語彙を形成します。識別子キーワード演算子と区切り記号リテラルの4つのクラスがあります。スペース (U+0020)、水平タブ (U+0009)、キャリッジリターン (U+000D)、改行 (U+000A) からなる空白は、それらが結合して単一のトークンになるのを防ぐ場合を除いて無視されます。また、改行またはファイルの終端がセミコロンの挿入をトリガーする場合があります。入力をトークンに分割する際、次のトークンは有効なトークンを形成する最長の文字シーケンスとなります。

セミコロン

形式構文では、セミコロン";"がいくつかのプロダクションの終端記号として使用されます。Goプログラムでは、以下の2つのルールを使用してこれらのセミコロンのほとんどを省略できます。

  1. 入力がトークンに分割されるとき、行の最後のトークンが以下のいずれかである場合、そのトークンの直後にセミコロンが自動的にトークンストリームに挿入されます。
  2. 複雑なステートメントを単一行に収めるために、閉じ括弧")"または"}"の前にセミコロンを省略することができます。

慣用的な使い方を反映するため、このドキュメントのコード例では、これらのルールに従ってセミコロンを省略しています。

識別子

識別子は、変数や型などのプログラムエンティティに名前を付けます。識別子は、1つ以上の文字と数字のシーケンスです。識別子の最初の文字は文字でなければなりません。

identifier = letter { letter | unicode_digit } .
a
_x9
ThisVariableIsExported
αβ

一部の識別子は事前宣言されています。

キーワード

以下のキーワードは予約されており、識別子として使用することはできません。

break        default      func         interface    select
case         defer        go           map          struct
chan         else         goto         package      switch
const        fallthrough  if           range        type
continue     for          import       return       var

演算子と句読点

以下の文字シーケンスは演算子代入演算子を含む)および句読点を表します[Go 1.18]

+    &     +=    &=     &&    ==    !=    (    )
-    |     -=    |=     ||    <     <=    [    ]
*    ^     *=    ^=     <-    >     >=    {    }
/    <<    /=    <<=    ++    =     :=    ,    ;
%    >>    %=    >>=    --    !     ...   .    :
     &^          &^=          ~

整数リテラル

整数リテラルは、整数定数を表す数字のシーケンスです。オプションの接頭辞は非10進数を設定します:2進数には0bまたは0B、8進数には00oまたは0O、16進数には0xまたは0X [Go 1.13]。単一の0は10進数のゼロと見なされます。16進数リテラルでは、文字aからfおよびAからFは値10から15を表します。

可読性のために、アンダースコア文字_は、基数プレフィックスの後または連続する数字の間に現れることがあります。このようなアンダースコアはリテラルの値を変更しません。

int_lit        = decimal_lit | binary_lit | octal_lit | hex_lit .
decimal_lit    = "0" | ( "1" … "9" ) [ [ "_" ] decimal_digits ] .
binary_lit     = "0" ( "b" | "B" ) [ "_" ] binary_digits .
octal_lit      = "0" [ "o" | "O" ] [ "_" ] octal_digits .
hex_lit        = "0" ( "x" | "X" ) [ "_" ] hex_digits .

decimal_digits = decimal_digit { [ "_" ] decimal_digit } .
binary_digits  = binary_digit { [ "_" ] binary_digit } .
octal_digits   = octal_digit { [ "_" ] octal_digit } .
hex_digits     = hex_digit { [ "_" ] hex_digit } .
42
4_2
0600
0_600
0o600
0O600       // second character is capital letter 'O'
0xBadFace
0xBad_Face
0x_67_7a_2f_cc_40_c6
170141183460469231731687303715884105727
170_141183_460469_231731_687303_715884_105727

_42         // an identifier, not an integer literal
42_         // invalid: _ must separate successive digits
4__2        // invalid: only one _ at a time
0_xBadFace  // invalid: _ must separate successive digits

浮動小数点リテラル

浮動小数点リテラルは、浮動小数点定数の10進または16進表現です。

10進浮動小数点リテラルは、整数部(10進数字)、小数点、小数部(10進数字)、および指数部(オプションの符号と10進数字が続くeまたはE)から構成されます。整数部または小数部のいずれかを省略でき、小数点または指数部のいずれかを省略できます。指数値expは、仮数(整数部と小数部)を10expでスケールします。

16進浮動小数点リテラルは、0xまたは0Xのプレフィックス、整数部(16進数字)、基数点、小数部(16進数字)、および指数部(オプションの符号と10進数字が続くpまたはP)から構成されます。整数部または小数部のいずれかを省略でき、基数点も省略できますが、指数部は必須です。(この構文はIEEE 754-2008 §5.12.3で与えられたものと一致します。)指数値expは、仮数(整数部と小数部)を2expでスケールします [Go 1.13]。

可読性のために、アンダースコア文字_は、基数プレフィックスの後または連続する数字の間に現れることがあります。このようなアンダースコアはリテラルの値を変更しません。

float_lit         = decimal_float_lit | hex_float_lit .

decimal_float_lit = decimal_digits "." [ decimal_digits ] [ decimal_exponent ] |
                    decimal_digits decimal_exponent |
                    "." decimal_digits [ decimal_exponent ] .
decimal_exponent  = ( "e" | "E" ) [ "+" | "-" ] decimal_digits .

hex_float_lit     = "0" ( "x" | "X" ) hex_mantissa hex_exponent .
hex_mantissa      = [ "_" ] hex_digits "." [ hex_digits ] |
                    [ "_" ] hex_digits |
                    "." hex_digits .
hex_exponent      = ( "p" | "P" ) [ "+" | "-" ] decimal_digits .
0.
72.40
072.40       // == 72.40
2.71828
1.e+0
6.67428e-11
1E6
.25
.12345E+5
1_5.         // == 15.0
0.15e+0_2    // == 15.0

0x1p-2       // == 0.25
0x2.p10      // == 2048.0
0x1.Fp+0     // == 1.9375
0X.8p-0      // == 0.5
0X_1FFFP-16  // == 0.1249847412109375
0x15e-2      // == 0x15e - 2 (integer subtraction)

0x.p1        // invalid: mantissa has no digits
1p-2         // invalid: p exponent requires hexadecimal mantissa
0x1.5e-2     // invalid: hexadecimal mantissa requires p exponent
1_.5         // invalid: _ must separate successive digits
1._5         // invalid: _ must separate successive digits
1.5_e1       // invalid: _ must separate successive digits
1.5e_1       // invalid: _ must separate successive digits
1.5e1_       // invalid: _ must separate successive digits

虚数リテラル

虚数リテラルは、複素数定数の虚数部を表します。整数または浮動小数点リテラルの後に小文字のiが続きます。虚数リテラルの値は、対応する整数または浮動小数点リテラルの値に虚数単位iを乗じたものです [Go 1.13]

imaginary_lit = (decimal_digits | int_lit | float_lit) "i" .

後方互換性のため、完全に10進数(および場合によってはアンダースコア)で構成される虚数リテラルの整数部は、先頭に0があっても10進整数とみなされます。

0i
0123i         // == 123i for backward-compatibility
0o123i        // == 0o123 * 1i == 83i
0xabci        // == 0xabc * 1i == 2748i
0.i
2.71828i
1.e+0i
6.67428e-11i
1E6i
.25i
.12345E+5i
0x1p-2i       // == 0x1p-2 * 1i == 0.25i

ルーンリテラル

ルーンリテラルは、Unicodeコードポイントを識別する整数値であるルーン定数を表します。ルーンリテラルは、'x''\n'のように、シングルクォートで囲まれた1つ以上の文字として表現されます。クォート内では、改行とエスケープされていないシングルクォートを除く任意の文字が現れることができます。単一引用符で囲まれた文字は、文字自体のUnicode値を表しますが、バックスラッシュで始まる複数文字シーケンスは、様々な形式で値をエンコードします。

最も単純な形式は、クォート内の単一文字を表します。GoのソーステキストはUTF-8でエンコードされたUnicode文字であるため、複数のUTF-8エンコードされたバイトが単一の整数値を表すことがあります。たとえば、リテラル'a'はリテラルa(Unicode U+0061、値0x61)を表す単一バイトを保持しますが、'ä'はリテラルa-分音符号(U+00E4、値0xe4)を表す2バイト(0xc3 0xa4)を保持します。

いくつかのバックスラッシュエスケープにより、任意の値をASCIIテキストとしてエンコードできます。整数値を数値定数として表現する方法は4つあります: \xの後に正確に2つの16進数字が続くもの; \uの後に正確に4つの16進数字が続くもの; \Uの後に正確に8つの16進数字が続くもの、およびプレーンなバックスラッシュ\の後に正確に3つの8進数字が続くものです。それぞれの場合において、リテラルの値は、対応する基数で数字によって表される値です。

これらの表現はすべて整数を結果としますが、有効範囲が異なります。8進数エスケープは0から255までの値を含む範囲を表さなければなりません。16進数エスケープは構成上この条件を満たします。\uおよび\UエスケープはUnicodeコードポイントを表すため、その中には一部の不正な値、特に0x10FFFFを超える値やサロゲートハーフは含まれません。

バックスラッシュの後、特定の単一文字エスケープは特殊な値を表します。

\a   U+0007 alert or bell
\b   U+0008 backspace
\f   U+000C form feed
\n   U+000A line feed or newline
\r   U+000D carriage return
\t   U+0009 horizontal tab
\v   U+000B vertical tab
\\   U+005C backslash
\'   U+0027 single quote  (valid escape only within rune literals)
\"   U+0022 double quote  (valid escape only within string literals)

ルーンリテラル内でバックスラッシュの後に認識できない文字が続くのは不正です。

rune_lit         = "'" ( unicode_value | byte_value ) "'" .
unicode_value    = unicode_char | little_u_value | big_u_value | escaped_char .
byte_value       = octal_byte_value | hex_byte_value .
octal_byte_value = `\` octal_digit octal_digit octal_digit .
hex_byte_value   = `\` "x" hex_digit hex_digit .
little_u_value   = `\` "u" hex_digit hex_digit hex_digit hex_digit .
big_u_value      = `\` "U" hex_digit hex_digit hex_digit hex_digit
                           hex_digit hex_digit hex_digit hex_digit .
escaped_char     = `\` ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | `\` | "'" | `"` ) .
'a'
'ä'
'本'
'\t'
'\000'
'\007'
'\377'
'\x07'
'\xff'
'\u12e4'
'\U00101234'
'\''         // rune literal containing single quote character
'aa'         // illegal: too many characters
'\k'         // illegal: k is not recognized after a backslash
'\xa'        // illegal: too few hexadecimal digits
'\0'         // illegal: too few octal digits
'\400'       // illegal: octal value over 255
'\uDFFF'     // illegal: surrogate half
'\U00110000' // illegal: invalid Unicode code point

文字列リテラル

文字列リテラルは、一連の文字を連結して得られる文字列定数を表します。形式は2種類あります: 生文字列リテラルと解釈文字列リテラルです。

生文字列リテラルは、`foo`のようにバッククォートで囲まれた文字シーケンスです。クォート内では、バッククォートを除く任意の文字が現れることができます。生文字列リテラルの値は、クォート間の解釈されていない(暗黙的にUTF-8エンコードされた)文字から構成される文字列です。特に、バックスラッシュは特別な意味を持たず、文字列には改行を含めることができます。生文字列リテラル内のキャリッジリターン文字('\r')は、生文字列値から破棄されます。

解釈文字列リテラルは、"bar"のように二重引用符で囲まれた文字シーケンスです。引用符内では、改行とエスケープされていない二重引用符を除く任意の文字が現れることができます。引用符間のテキストはリテラルの値を形成し、ルーンリテラルと同様にバックスラッシュエスケープが解釈されます(ただし、\'は不正で\"は合法です)。3桁の8進数(\nnn)および2桁の16進数(\xnn)エスケープは、結果の文字列の個々のバイトを表します。他のすべてのエスケープは、個々の文字の(場合によっては複数バイトの)UTF-8エンコーディングを表します。したがって、文字列リテラル\377\xFFは値0xFF=255の単一バイトを表しますが、ÿ\u00FF\U000000FF、および\xc3\xbfは、文字U+00FFのUTF-8エンコーディングの2バイト0xc3 0xbfを表します。

string_lit             = raw_string_lit | interpreted_string_lit .
raw_string_lit         = "`" { unicode_char | newline } "`" .
interpreted_string_lit = `"` { unicode_value | byte_value } `"` .
`abc`                // same as "abc"
`\n
\n`                  // same as "\\n\n\\n"
"\n"
"\""                 // same as `"`
"Hello, world!\n"
"日本語"
"\u65e5本\U00008a9e"
"\xff\u00FF"
"\uD800"             // illegal: surrogate half
"\U00110000"         // illegal: invalid Unicode code point

これらの例はすべて同じ文字列を表します

"日本語"                                 // UTF-8 input text
`日本語`                                 // UTF-8 input text as a raw literal
"\u65e5\u672c\u8a9e"                    // the explicit Unicode code points
"\U000065e5\U0000672c\U00008a9e"        // the explicit Unicode code points
"\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"  // the explicit UTF-8 bytes

ソースコードがアクセントと文字を含む合成形式など、2つのコードポイントとして文字を表す場合、ルーンリテラルに配置するとエラーになります(単一のコードポイントではないため)。文字列リテラルに配置すると、2つのコードポイントとして表示されます。

定数

ブール定数ルーン定数整数定数浮動小数点定数複素数定数、および文字列定数があります。ルーン、整数、浮動小数点、および複素数定数は、まとめて数値定数と呼ばれます。

定数値は、ルーン整数浮動小数点虚数、または文字列リテラル、定数を表す識別子、定数式、結果が定数である変換、または定数引数に適用されるminmaxなどの組み込み関数、特定の値に適用されるunsafe.Sizeof一部の式に適用されるcaplen、複素数定数に適用されるrealimag、数値定数に適用されるcomplexの結果値によって表されます。ブール真理値は、事前宣言された定数truefalseによって表されます。事前宣言された識別子iotaは整数定数を表します。

一般的に、複素数定数は定数式の一種であり、そのセクションで説明されます。

数値定数は、任意精度の正確な値を表し、オーバーフローしません。したがって、IEEE 754の負のゼロ、無限大、NaNの値を表す定数はありません。

定数には型付きまたは型なしがあります。リテラル定数、truefalseiota、および型なし定数オペランドのみを含む特定の定数式は型なしです。

定数には、定数宣言または変換によって明示的に型を与えることができます。または、変数宣言代入ステートメントで使用された場合、あるいはのオペランドとして使用された場合に暗黙的に型が与えられます。定数値が該当する型の値として表現できない場合はエラーとなります。型が型パラメータの場合、定数は型パラメータの非定数値に変換されます。

型なし定数にはデフォルト型があります。これは、型付き値が要求される文脈、例えば明示的な型がないi := 0のような短変数宣言において、定数が暗黙的に変換される型です。型なし定数のデフォルト型は、それがブール、ルーン、整数、浮動小数点、複素数、または文字列定数であるかに応じて、それぞれboolruneintfloat64complex128、またはstringです。

実装上の制約:数値定数は言語では任意精度ですが、コンパイラは制限された精度の内部表現を使用してそれらを実装する場合があります。ただし、すべての実装は次のことを行わなければなりません。

  • 整数定数を少なくとも256ビットで表現します。
  • 複素数定数の部分を含む浮動小数点定数を、少なくとも256ビットの仮数と少なくとも16ビットの符号付きバイナリ指数で表現します。
  • 整数定数を正確に表現できない場合はエラーを出します。
  • オーバーフローにより浮動小数点または複素数定数を表現できない場合はエラーを出します。
  • 精度制限により浮動小数点または複素数定数を表現できない場合は、最も近い表現可能な定数に丸めます。

これらの要件は、リテラル定数と定数式の評価結果の両方に適用されます。

変数

変数は、を保持するための記憶場所です。許容される値の集合は、変数のによって決定されます。

変数宣言、または関数パラメータと結果の場合、関数宣言または関数リテラルのシグネチャは、名前付き変数のためのストレージを予約します。組み込み関数newを呼び出すか、複合リテラルのアドレスを取ることは、実行時に変数のためのストレージを割り当てます。このような匿名変数は、(おそらく暗黙的な)ポインタ間接参照を介して参照されます。

配列スライス構造体型の構造化変数には、個別にアドレス指定できる要素とフィールドがあります。各そのような要素は変数のように機能します。

変数の静的型(または単に)は、その宣言で与えられた型、new呼び出しまたは複合リテラルで提供された型、あるいは構造化変数の要素の型です。インターフェース型の変数には、異なる動的型もあります。これは、実行時に変数に割り当てられた値の(非インターフェース)型です(ただし、値が型を持たない事前宣言された識別子nilの場合を除く)。動的型は実行中に変化する可能性がありますが、インターフェース変数に格納される値は常に変数の静的型に代入可能です。

var x interface{}  // x is nil and has static type interface{}
var v *T           // v has value nil, static type *T
x = 42             // x has value 42 and dynamic type int
x = v              // x has value (*T)(nil) and dynamic type *T

変数の値は、で変数を参照することによって取得されます。これは、変数に割り当てられた最新の値です。変数にまだ値が割り当てられていない場合、その値は型のゼロ値です。

型は、値の集合と、それらの値に特有の操作およびメソッドを決定します。型は、もしあれば型名で表すことができ、その型がジェネリックである場合は型引数が続かなければなりません。型は、既存の型から型を構成する型リテラルを使用しても指定できます。

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

言語は特定の型名を事前宣言します。他の型は型宣言または型パラメータリストで導入されます。複合型 — 配列、構造体、ポインタ、関数、インターフェース、スライス、マップ、およびチャネル型 — は型リテラルを使用して構築できます。

事前宣言された型、定義された型、および型パラメータは、名前付き型と呼ばれます。エイリアス宣言で与えられた型が名前付き型である場合、エイリアスは名前付き型を示します。

ブール型

ブール型は、事前宣言された定数truefalseで示されるブール真理値の集合を表します。事前宣言されたブール型はboolです。これは定義された型です。

数値型

整数浮動小数点、または複素数型は、それぞれ整数、浮動小数点、または複素数の値の集合を表します。これらはまとめて数値型と呼ばれます。事前宣言されたアーキテクチャに依存しない数値型は次のとおりです。

uint8       the set of all unsigned  8-bit integers (0 to 255)
uint16      the set of all unsigned 16-bit integers (0 to 65535)
uint32      the set of all unsigned 32-bit integers (0 to 4294967295)
uint64      the set of all unsigned 64-bit integers (0 to 18446744073709551615)

int8        the set of all signed  8-bit integers (-128 to 127)
int16       the set of all signed 16-bit integers (-32768 to 32767)
int32       the set of all signed 32-bit integers (-2147483648 to 2147483647)
int64       the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)

float32     the set of all IEEE 754 32-bit floating-point numbers
float64     the set of all IEEE 754 64-bit floating-point numbers

complex64   the set of all complex numbers with float32 real and imaginary parts
complex128  the set of all complex numbers with float64 real and imaginary parts

byte        alias for uint8
rune        alias for int32

nビット整数の値はnビット幅であり、2の補数演算を使用して表現されます。

実装固有のサイズの事前宣言された整数型のセットもあります。

uint     either 32 or 64 bits
int      same size as uint
uintptr  an unsigned integer large enough to store the uninterpreted bits of a pointer value

移植性の問題を避けるため、すべての数値型は定義された型であり、byteuint8エイリアス)とruneint32のエイリアス)を除いては異なるものです。異なる数値型が式や代入で混在する場合、明示的な変換が必要です。たとえば、int32intは、特定のアーキテクチャで同じサイズを持つ可能性があっても、同じ型ではありません。

文字列型

文字列型は文字列値の集合を表します。文字列値は(おそらく空の)バイトのシーケンスです。バイト数は文字列の長さと呼ばれ、決して負になりません。文字列は不変です。一度作成されると、文字列の内容を変更することはできません。事前宣言された文字列型はstringです。これは定義された型です。

文字列sの長さは、組み込み関数lenを使用して取得できます。文字列が定数の場合、長さはコンパイル時定数です。文字列のバイトは、整数インデックス0からlen(s)-1まででアクセスできます。そのような要素のアドレスを取ることは不正です。s[i]が文字列のi番目のバイトである場合、&s[i]は無効です。

配列型

配列は、要素型と呼ばれる単一の型の要素の番号付けされたシーケンスです。要素の数は配列の長さと呼ばれ、決して負になりません。

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  // same as [2]([2]([2]float64))

配列型Tは、その要素型がTである要素を持つことはできません。また、それを含む型が配列型または構造体型のみの場合、直接的または間接的にTをコンポーネントとして含む型を持つこともできません。

// invalid array types
type (
	T1 [10]T1                 // element type of T1 is T1
	T2 [10]struct{ f T2 }     // T2 contains T2 as component of a struct
	T3 [10]T4                 // T3 contains T3 as component of a struct in T4
	T4 struct{ f T3 }         // T4 contains T4 as component of array T3 in a struct
)

// valid array types
type (
	T5 [10]*T5                // T5 contains T5 as component of a pointer
	T6 [10]func() T6          // T6 contains T6 as component of a function type
	T7 [10]struct{ f []T7 }   // T7 contains T7 as component of a slice in a struct
)

スライス型

スライスは、基底配列の連続したセグメントの記述子であり、その配列からの要素の番号付けされたシーケンスへのアクセスを提供します。スライス型は、その要素型の配列のすべてのスライスの集合を表します。要素の数はスライスの長さと呼ばれ、決して負になりません。初期化されていないスライスの値はnilです。

SliceType = "[" "]" ElementType .

スライスsの長さは、組み込み関数lenによって検出できます。配列とは異なり、実行中に変化する可能性があります。要素は、整数インデックス0からlen(s)-1まででアドレス指定できます。特定の要素のスライスインデックスは、基底配列内の同じ要素のインデックスよりも小さい場合があります。

スライスは、一度初期化されると、常にその要素を保持する基底配列に関連付けられます。したがって、スライスはその配列および同じ配列の他のスライスとストレージを共有します。対照的に、異なる配列は常に異なるストレージを表します。

スライスの基底配列は、スライスの末尾を超えて拡張されることがあります。容量はその範囲の尺度であり、スライスの長さとスライスを超えた配列の長さの合計です。その容量までの長さのスライスは、元のスライスから新しいスライスをスライスすることによって作成できます。スライスaの容量は、組み込み関数cap(a)を使用して取得できます。

特定の要素型Tの新しい初期化されたスライス値は、組み込み関数makeを使用して作成できます。この関数はスライス型と、長さおよびオプションで容量を指定するパラメータを取ります。makeで作成されたスライスは常に新しい隠された配列を割り当て、返されるスライス値がそれを参照します。つまり、

make([]T, length, capacity)

は、配列を割り当ててスライスするのと同じスライスを生成するため、これら2つの式は同等です。

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

配列と同様に、スライスは常に1次元ですが、より高次元のオブジェクトを構築するために構成することができます。配列の配列の場合、内側の配列は、構成上、常に同じ長さになりますが、スライスのスライス(またはスライスの配列)の場合、内側の長さは動的に変化する可能性があります。さらに、内側のスライスは個別に初期化する必要があります。

構造体型

構造体は、フィールドと呼ばれる名前付き要素のシーケンスであり、それぞれが名前と型を持ちます。フィールド名は明示的に (IdentifierList) または暗黙的に (EmbeddedField) 指定できます。構造体内で、非空白フィールド名は一意でなければなりません。

StructType    = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl     = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ "*" ] TypeName [ TypeArgs ] .
Tag           = string_lit .
// An empty struct.
struct {}

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

型が宣言されているが明示的なフィールド名がないフィールドは、埋め込みフィールドと呼ばれます。埋め込みフィールドは、型名Tとして、または非インターフェース型名*Tへのポインタとして指定されなければならず、T自体はポインタ型または型パラメータであってはなりません。修飾されていない型名がフィールド名として機能します。

// A struct with four embedded fields of types T1, *T2, P.T3 and *P.T4
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     // conflicts with embedded field *T and *P.T
	*T    // conflicts with embedded field T and *P.T
	*P.T  // conflicts with embedded field T and *T
}

構造体xの埋め込みフィールドのフィールドまたはメソッドfは、x.fがそのフィールドまたはメソッドfを示す有効なセレクターである場合、昇格と呼ばれます。

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

構造体型Sと型名Tが与えられた場合、昇格されたメソッドは次のように構造体のメソッドセットに含まれます。

  • Sが埋め込みフィールドTを含む場合、Sおよび*Sメソッドセットの両方には、レシーバーTを持つ昇格されたメソッドが含まれます。*Sのメソッドセットには、レシーバー*Tを持つ昇格されたメソッドも含まれます。
  • Sが埋め込みフィールド*Tを含む場合、Sおよび*Sのメソッドセットの両方には、レシーバーTまたは*Tを持つ昇格されたメソッドが含まれます。

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

struct {
	x, y float64 ""  // an empty tag string is like an absent tag
	name string  "any string is permitted as a tag"
	_    [4]byte "ceci n'est pas un champ de structure"
}

// A struct corresponding to a TimeStamp protocol buffer.
// The tag strings define the protocol buffer field numbers;
// they follow the convention outlined by the reflect package.
struct {
	microsec  uint64 `protobuf:"1"`
	serverIP6 uint64 `protobuf:"2"`
}

構造体型Tは、その要素型がTであるフィールドを持つことはできません。また、それを含む型が配列型または構造体型のみの場合、直接的または間接的にTをコンポーネントとして含む型を持つこともできません。

// invalid struct types
type (
	T1 struct{ T1 }            // T1 contains a field of T1
	T2 struct{ f [10]T2 }      // T2 contains T2 as component of an array
	T3 struct{ T4 }            // T3 contains T3 as component of an array in struct T4
	T4 struct{ f [10]T3 }      // T4 contains T4 as component of struct T3 in an array
)

// valid struct types
type (
	T5 struct{ f *T5 }         // T5 contains T5 as component of a pointer
	T6 struct{ f func() T6 }   // T6 contains T6 as component of a function type
	T7 struct{ f [10][]T7 }    // T7 contains T7 as component of a slice in an array
)

ポインタ型

ポインタ型は、基底型と呼ばれる特定の型の変数へのすべてのポインタの集合を表します。初期化されていないポインタのnilです。

PointerType = "*" BaseType .
BaseType    = Type .
*Point
*[4]int

関数型

関数型は、同じパラメータ型と結果型を持つすべての関数の集合を表します。関数型の初期化されていない変数のnilです。

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

パラメータまたは結果のリスト内では、名前(IdentifierList)はすべて存在するか、すべて存在しないかのどちらかでなければなりません。存在する場合、各名前は指定された型の1つの項目(パラメータまたは結果)を表し、シグネチャ内のすべての非空白の名前は一意でなければなりません。存在しない場合、各型はその型の1つの項目を表します。パラメータおよび結果リストは常に括弧で囲まれますが、ただし、名前のない結果が1つだけの場合、括弧なしの型として記述できます。

関数シグネチャの最後の入力パラメータは、...で始まる型を持つことができます。そのようなパラメータを持つ関数は可変長引数と呼ばれ、そのパラメータに対して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)

インターフェース型

インターフェース型は型セットを定義します。インターフェース型の変数は、インターフェースの型セットにある任意の型の値を格納できます。そのような型はインターフェースを実装していると言われます。初期化されていないインターフェース型のnilです。

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

インターフェース型は、インターフェース要素のリストによって指定されます。インターフェース要素はメソッドまたは型要素のいずれかであり、型要素は1つ以上の型項のユニオンです。型項は単一の型または単一の基底型のいずれかです。

基本インターフェース

最も基本的な形式では、インターフェースは(おそらく空の)メソッドのリストを指定します。そのようなインターフェースによって定義される型セットは、それらのすべてのメソッドを実装する型のセットであり、対応するメソッドセットはインターフェースによって指定されたメソッドのみから構成されます。型セットが完全にメソッドのリストによって定義できるインターフェースは基本インターフェースと呼ばれます。

// A simple File interface.
interface {
	Read([]byte) (int, error)
	Write([]byte) (int, error)
	Close() error
}

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

interface {
	String() string
	String() string  // illegal: String not unique
	_(x int)         // illegal: method must have non-blank name
}

複数の型がインターフェースを実装できます。例えば、2つの型S1S2がメソッドセットを持っている場合

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

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

インターフェースの型セットのメンバーであるすべての型は、そのインターフェースを実装します。任意の特定の型は、いくつかの異なるインターフェースを実装できます。たとえば、すべての型は、すべての(非インターフェース)型の集合を表す空のインターフェースを実装します。

interface{}

便宜上、事前宣言された型anyは空のインターフェースのエイリアスです。 [Go 1.18]

同様に、Lockerと呼ばれるインターフェースを定義するために型宣言内に現れるこのインターフェース仕様を検討します。

type Locker interface {
	Lock()
	Unlock()
}

S1S2も実装する場合

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

それらはLockerインターフェースとFileインターフェースの両方を実装します。

埋め込みインターフェース

わずかに一般的な形式では、インターフェースTは、(おそらく修飾された)インターフェース型名Eをインターフェース要素として使用することができます。これはインターフェースET埋め込むと呼ばれます [Go 1.14]。Tの型セットは、Tによって明示的に宣言されたメソッドの型セットと、Tの埋め込みインターフェースの型セットの交差です。言い換えれば、Tの型セットは、Tのすべての明示的に宣言されたメソッドとEのすべてのメソッドを実装するすべての型のセットです [Go 1.18]。

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's methods are Read, Write, and Close.
type ReadWriter interface {
	Reader  // includes methods of Reader in ReadWriter's method set
	Writer  // includes methods of Writer in ReadWriter's method set
}

インターフェースを埋め込む際、同じ名前のメソッドは同一のシグネチャを持つ必要があります。

type ReadCloser interface {
	Reader   // includes methods of Reader in ReadCloser's method set
	Close()  // illegal: signatures of Reader.Close and Close are different
}

一般インターフェース

最も一般的な形式では、インターフェース要素は任意の型項T、または基底型Tを指定する~Tの形式の項、またはt1|t2|…|tnの項のユニオンであることもあります [Go 1.18]。メソッド仕様と合わせて、これらの要素はインターフェースの型セットを次のように正確に定義することを可能にします。

  • 空のインターフェースの型セットは、すべての非インターフェース型のセットです。
  • 空でないインターフェースの型セットは、そのインターフェース要素の型セットの交差です。
  • メソッド仕様の型セットは、そのメソッドをメソッドセットに含むすべての非インターフェース型のセットです。
  • 非インターフェース型項の型セットは、その型のみからなるセットです。
  • ~Tの形式の項の型セットは、基底型がTであるすべての型のセットです。
  • 項のユニオンt1|t2|…|tnの型セットは、各項の型セットのユニオンです。

"すべての非インターフェース型の集合"という数量化は、手元のプログラムで宣言されたすべての(非インターフェース)型だけでなく、すべての可能なプログラムにおけるすべての可能な型を指し、したがって無限です。同様に、特定のメソッドを実装するすべての非インターフェース型の集合が与えられた場合、それらの型のメソッド集合の交差は、手元のプログラムのすべての型が常にそのメソッドを別のメソッドとペアにする場合でも、正確にそのメソッドを含みます。

構造上、インターフェースの型セットはインターフェース型を決して含みません。

// An interface representing only the type int.
interface {
	int
}

// An interface representing all types with underlying type int.
interface {
	~int
}

// An interface representing all types with underlying type int that implement the String method.
interface {
	~int
	String() string
}

// An interface representing an empty type set: there is no type that is both an int and a string.
interface {
	int
	string
}

~Tの形式の項では、Tの基底型はそれ自体でなければならず、Tはインターフェースであってはなりません。

type MyInt int

interface {
	~[]byte  // the underlying type of []byte is itself
	~MyInt   // illegal: the underlying type of MyInt is not MyInt
	~error   // illegal: error is an interface
}

ユニオン要素は型セットのユニオンを示します。

// The Float interface represents all floating-point types
// (including any named types whose underlying types are
// either float32 or float64).
type Float interface {
	~float32 | ~float64
}

Tまたは~Tの形式の項の型T型パラメータであってはならず、すべての非インターフェース項の型セットはペアで互いに素でなければなりません(型セットのペアごとの交差は空でなければなりません)。型パラメータPが与えられた場合

interface {
	P                // illegal: P is a type parameter
	int | ~P         // illegal: P is a type parameter
	~int | MyInt     // illegal: the type sets for ~int and MyInt are not disjoint (~int includes MyInt)
	float32 | Float  // overlapping type sets but Float is an interface
}

実装上の制約:ユニオン(複数の項を持つ場合)は、事前宣言された識別子comparableやメソッドを指定するインターフェース、またはcomparableやメソッドを指定するインターフェースを埋め込むことはできません。

基本ではないインターフェースは、型制約としてのみ、または制約として使用される他のインターフェースの要素としてのみ使用できます。それらは値や変数の型、または他の非インターフェース型のコンポーネントになることはできません。

var x Float                     // illegal: Float is not a basic interface

var x interface{} = Float(nil)  // illegal

type Floatish struct {
	f Float                 // illegal
}

インターフェース型Tは、直接的または間接的にTである、Tを含む、またはTを埋め込む型要素を埋め込むことはできません。

// illegal: Bad may not embed itself
type Bad interface {
	Bad
}

// illegal: Bad1 may not embed itself using Bad2
type Bad1 interface {
	Bad2
}
type Bad2 interface {
	Bad1
}

// illegal: Bad3 may not embed a union containing Bad3
type Bad3 interface {
	~int | ~string | Bad3
}

// illegal: Bad4 may not embed an array containing Bad4 as element type
type Bad4 interface {
	[10]Bad4
}

インターフェースの実装

TがインターフェースIを実装するのは、次のいずれかの条件が当てはまる場合です。

  • Tがインターフェースではなく、Iの型セットの要素である場合、または
  • Tがインターフェースであり、Tの型セットがIの型セットのサブセットである場合。

Tの値は、Tがインターフェースを実装していれば、インターフェースを実装します。

マップ型

マップは、要素型と呼ばれる1つの型の要素の順序付けされていないグループであり、キー型と呼ばれる別の型の固有のキーのセットによってインデックスが付けられます。初期化されていないマップのnilです。

MapType = "map" "[" KeyType "]" ElementType .
KeyType = Type .

キー型のオペランドに対して、比較演算子==!=は完全に定義されていなければなりません。したがって、キー型は関数、マップ、スライスであってはなりません。キー型がインターフェース型の場合、これらの比較演算子は動的なキー値に対して定義されていなければなりません。失敗するとランタイムパニックが発生します。

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

マップ要素の数をその長さと呼びます。マップmの場合、組み込み関数lenを使用して検出でき、実行中に変化する可能性があります。要素は代入を使用して実行中に追加でき、インデックス式で取得できます。組み込み関数deleteclearで削除できます。

新しい空のマップ値は、組み込み関数makeを使用して作成されます。これはマップ型とオプションの容量ヒントを引数として取ります。

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

初期容量はそのサイズを制限しません。マップは、nilマップを除いて、格納されるアイテムの数に対応するように拡張されます。nilマップは空のマップと同じですが、要素を追加できない点が異なります。

チャネルは、並行して実行される関数が指定された要素型の値を送信および受信することによって通信するためのメカニズムを提供します。初期化されていないチャネルのnilです。

チャネルは、並行して実行される関数が指定された要素型の値を送信および受信することによって通信するためのメカニズムを提供します。初期化されていないチャネルのnilです。

ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .

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

chan T          // can be used to send and receive values of type T
chan<- float64  // can only be used to send float64s
<-chan int      // can only be used to receive ints

<-演算子は、可能な限り左のchanと結合します。

chan<- chan int    // same as chan<- (chan int)
chan<- <-chan int  // same as chan<- (<-chan int)
<-chan <-chan int  // same as <-chan (<-chan int)
chan (<-chan int)

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

make(chan int, 100)

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

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

単一のチャネルは、追加の同期なしに、任意の数のゴルーチンによって送信ステートメント受信操作、および組み込み関数capおよびlenの呼び出しで使用できます。チャネルは先入れ先出しのキューとして機能します。例えば、1つのゴルーチンがチャネルに値を送信し、2番目のゴルーチンがそれらを受信する場合、値は送信された順序で受信されます。

型と値のプロパティ

値の表現

事前宣言された型(anyerrorインターフェースについては下記参照)、配列、および構造体の値は自己完結型です。それぞれの値はすべてのデータの完全なコピーを含み、そのような型の変数は値全体を格納します。例えば、配列変数は配列のすべての要素のためのストレージ(変数)を提供します。それぞれのゼロ値は値の型に固有であり、決してnilではありません。

nilでないポインタ、関数、スライス、マップ、およびチャネルの値は、複数の値によって共有される可能性のある基底データへの参照を含みます。

  • ポインタ値は、ポインタ基底型の値を保持する変数への参照です。
  • 関数値は、(おそらく匿名の)関数と囲まれた変数への参照を含みます。
  • スライス値は、スライスの長さ、容量、およびその基底配列への参照を含みます。
  • マップまたはチャネル値は、マップまたはチャネルの実装固有のデータ構造への参照です。

インターフェース値は、インターフェースの動的型に応じて、自己完結型であるか、基底データへの参照を含む場合があります。事前宣言された識別子nilは、値が参照を含むことができる型のゼロ値です。

複数の値が基盤となるデータを共有する場合、1つの値を変更すると別の値も変更される可能性があります。たとえば、スライスの要素を変更すると、その配列を共有するすべてのスライスの基盤となる配列のその要素が変更されます。

基底型

各型Tには基底型があります。Tが事前宣言されたブール型、数値型、文字列型のいずれか、または型リテラルの場合、対応する基底型はT自体です。そうでない場合、Tの基底型は、宣言でTが参照する型の基底型です。型パラメータの場合、それはその型制約の基底型であり、常にインターフェースです。

type (
	A1 = string
	A2 = A1
)

type (
	B1 string
	B2 B1
	B3 []B1
	B4 B3
)

func f[P any](x P) { … }

stringA1A2B1、およびB2の基底型はstringです。[]B1B3、およびB4の基底型は[]B1です。Pの基底型はinterface{}です。

型の同一性

2つの型は同一である(「同じ」である)か、異なるかのどちらかです。

名前付き型は常に他の型とは異なります。それ以外の場合、2つの型は、その基底型リテラルが構造的に同等である場合に同一です。つまり、同じリテラル構造を持ち、対応するコンポーネントが同一の型を持つ場合です。詳細には

  • 2つの配列型は、要素型が同一であり、配列の長さが同じである場合に同一です。
  • 2つのスライス型は、要素型が同一である場合に同一です。
  • 2つの構造体型は、フィールドのシーケンスが同じであり、対応するフィールドのペアが同じ名前、同一の型、同一のタグを持ち、両方とも埋め込まれているか、両方とも埋め込まれていない場合に同一です。非エクスポートフィールド名は、異なるパッケージからは常に異なります。
  • 2つのポインタ型は、基底型が同一である場合に同一です。
  • 2つの関数型は、パラメータと結果の数が同じであり、対応するパラメータと結果の型が同一であり、かつ両方の関数が可変長引数であるか、どちらも可変長引数でない場合に同一です。パラメータと結果の名前が一致する必要はありません。
  • 2つのインターフェース型は、同じ型セットを定義する場合に同一です。
  • 2つのマップ型は、キーと要素の型が同一である場合に同一です。
  • 2つのチャネル型は、要素型が同一であり、方向が同じである場合に同一です。
  • 2つのインスタンス化された型は、その定義された型とすべての型引数が同一である場合に同一です。

宣言が与えられた場合

type (
	A0 = []string
	A1 = A0
	A2 = struct{ a, b int }
	A3 = int
	A4 = func(A3, float64) *A0
	A5 = func(x int, _ float64) *[]string

	B0 A0
	B1 []string
	B2 struct{ a, b int }
	B3 struct{ a, c int }
	B4 func(int, float64) *B0
	B5 func(x int, y float64) *A1

	C0 = B0
	D0[P1, P2 any] struct{ x P1; y P2 }
	E0 = D0[int, string]
)

これらの型は同一です。

A0, A1, and []string
A2 and struct{ a, b int }
A3 and int
A4, func(int, float64) *[]string, and A5

B0 and C0
D0[int, string] and E0
[]int and []int
struct{ a, b *B5 } and struct{ a, b *B5 }
func(x int, y float64) *[]string, func(int, float64) (result *[]string), and A5

B0B1は、異なる型定義によって作成された新しい型であるため異なります。func(int, float64) *B0func(x int, y float64) *[]stringは、B0[]stringと異なるため異なります。P1P2は、異なる型パラメータであるため異なります。D0[int, string]struct{ x int; y string }は、前者がインスタンス化された定義された型であるのに対し、後者が型リテラルであるため異なります(ただし、依然として代入可能です)。

代入可能性

Vの値xが型T変数代入可能である(「xTに代入可能である」)のは、以下のいずれかの条件が適用される場合です。

  • VTは同一です。
  • VTは同一の基底型を持ちますが、型パラメータではなく、VまたはTの少なくとも一方が名前付き型ではありません。
  • VTは、要素型が同一のチャネル型であり、Vは双方向チャネルであり、VまたはTの少なくとも一方が名前付き型ではありません。
  • Tがインターフェース型であり、型パラメータではない場合、xT実装します。
  • xは事前宣言された識別子nilであり、Tはポインタ、関数、スライス、マップ、チャネル、またはインターフェース型であり、型パラメータではない場合。
  • xは、型Tの値として表現可能な型なしの定数です。

さらに、xの型VまたはTが型パラメータである場合、次のいずれかの条件が適用されると、xは型Tの変数に代入可能です。

  • xは事前宣言された識別子nilであり、Tは型パラメータであり、xTの型セット内の各型に代入可能です。
  • V名前付き型ではなく、Tは型パラメータであり、xTの型セット内の各型に代入可能です。
  • Vは型パラメータであり、Tは名前付き型ではなく、Vの型セット内の各型の値はTに代入可能です。

表現可能性

定数xが型T型パラメータではない)の値によって表現可能であるのは、以下のいずれかの条件が当てはまる場合です。

  • xTによって決定される値の集合に含まれます。
  • T浮動小数点型であり、xはオーバーフローなしでTの精度に丸めることができます。丸めはIEEE 754の偶数への丸めルールを使用しますが、IEEEの負のゼロは符号なしゼロにさらに簡略化されます。定数値は決してIEEEの負のゼロ、NaN、または無限大にならないことに注意してください。
  • Tが複素数型であり、xコンポーネントreal(x)およびimag(x)が、Tのコンポーネント型(float32またはfloat64)の値で表現可能である場合。

Tが型パラメータの場合、xTの型セット内の各型の値で表現可能であれば、xは型Tの値で表現可能です。

x                   T           x is representable by a value of T because

'a'                 byte        97 is in the set of byte values
97                  rune        rune is an alias for int32, and 97 is in the set of 32-bit integers
"foo"               string      "foo" is in the set of string values
1024                int16       1024 is in the set of 16-bit integers
42.0                byte        42 is in the set of unsigned 8-bit integers
1e10                uint64      10000000000 is in the set of unsigned 64-bit integers
2.718281828459045   float32     2.718281828459045 rounds to 2.7182817 which is in the set of float32 values
-1e-1000            float64     -1e-1000 rounds to IEEE -0.0 which is further simplified to 0.0
0i                  int         0 is an integer value
(42 + 0i)           float32     42.0 (with zero imaginary part) is in the set of float32 values
x                   T           x is not representable by a value of T because

0                   bool        0 is not in the set of boolean values
'a'                 string      'a' is a rune, it is not in the set of string values
1024                byte        1024 is not in the set of unsigned 8-bit integers
-1                  uint16      -1 is not in the set of unsigned 16-bit integers
1.1                 int         1.1 is not an integer value
42i                 float32     (0 + 42i) is not in the set of float32 values
1e1000              float64     1e1000 overflows to IEEE +Inf after rounding

メソッドセット

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

  • 定義された型Tのメソッドセットは、レシーバ型Tで宣言されたすべてのメソッドから構成されます。
  • 定義された型Tへのポインタ(Tがポインタでもインターフェースでもない場合)のメソッドセットは、レシーバ*TまたはTで宣言されたすべてのメソッドの集合です。
  • インターフェース型のメソッドセットは、インターフェースの型セット内の各型のメソッドセットの交差です(結果として得られるメソッドセットは、通常、インターフェースで宣言されたメソッドの集合にすぎません)。

埋め込みフィールドを含む構造体(および構造体へのポインタ)には、構造体型のセクションで説明されている追加のルールが適用されます。その他の型は空のメソッドセットを持ちます。

メソッドセットでは、各メソッドは一意の非空白メソッド名を持つ必要があります。

ブロック

ブロックは、対応する中括弧で囲まれた、宣言とステートメントの、おそらく空のシーケンスです。

Block         = "{" StatementList "}" .
StatementList = { Statement ";" } .

ソースコード内の明示的なブロックに加えて、暗黙的なブロックがあります。

  1. ユニバースブロックは、すべてのGoソーステキストを包含します。
  2. パッケージには、そのパッケージのすべてのGoソーステキストを含むパッケージブロックがあります。
  3. 各ファイルには、そのファイル内のすべてのGoソーステキストを含むファイルブロックがあります。
  4. 「if」「for」、および「switch」ステートメントは、それぞれ独自の暗黙的なブロックにあると見なされます。
  5. "switch"または"select"ステートメントの各句は暗黙のブロックとして機能します。

ブロックは入れ子になり、スコープに影響を与えます。

宣言とスコープ

宣言は、非空白の識別子を定数型パラメータ変数関数ラベル、またはパッケージにバインドします。プログラム内のすべての識別子は宣言されなければなりません。同じブロック内で識別子が二度宣言されることはなく、ファイルブロックとパッケージブロックの両方で識別子が宣言されることもありません。

空白識別子は宣言において他の識別子と同様に使用できますが、バインディングを導入しないため宣言されません。パッケージブロックでは、識別子initinit関数の宣言にのみ使用でき、空白識別子と同様に新しいバインディングを導入しません。

Declaration  = ConstDecl | TypeDecl | VarDecl .
TopLevelDecl = Declaration | FunctionDecl | MethodDecl .

宣言された識別子のスコープは、識別子が指定された定数、型、変数、関数、ラベル、またはパッケージを示すソーステキストの範囲です。

Goはブロックを使用して字句スコープを持ちます。

  1. 事前宣言された識別子のスコープはユニバースブロックです。
  2. トップレベル(関数外)で宣言された定数、型、変数、または関数(ただしメソッドではない)を示す識別子のスコープは、パッケージブロックです。
  3. インポートされたパッケージのパッケージ名のスコープは、インポート宣言を含むファイルのファイルブロックです。
  4. メソッドレシーバ、関数パラメータ、または結果変数を表す識別子のスコープは、関数本体です。
  5. 関数の型パラメータまたはメソッドレシーバによって宣言された型パラメータを表す識別子のスコープは、関数名の後から関数本体の終わりまでです。
  6. 型の型パラメータを示す識別子のスコープは、型名の後からTypeSpecの終わりまでです。
  7. 関数内で宣言された定数または変数識別子のスコープは、ConstSpecまたはVarSpec(短変数宣言の場合はShortVarDecl)の終わりから、最も内側の包含ブロックの終わりまでです。
  8. 関数内で宣言された型識別子のスコープは、TypeSpec内の識別子から最も内側の包含ブロックの終わりまでです。

ブロック内で宣言された識別子は、内側のブロックで再宣言される場合があります。内側の宣言の識別子がスコープ内にある間は、内側の宣言によって宣言されたエンティティを示します。

パッケージ句は宣言ではありません。パッケージ名はどのスコープにも現れません。その目的は、同じパッケージに属するファイルを識別し、インポート宣言のデフォルトパッケージ名を指定することです。

ラベルスコープ

ラベルはラベル付きステートメントによって宣言され、"break""continue"、および"goto"ステートメントで使用されます。使用されていないラベルを定義することは不正です。他の識別子とは対照的に、ラベルはブロックスコープではなく、ラベルではない識別子と競合しません。ラベルのスコープは、それが宣言された関数の本体であり、入れ子になった関数の本体は除外されます。

空白識別子

空白識別子はアンダースコア文字_で表されます。これは通常の(非空白の)識別子の代わりに匿名プレースホルダーとして機能し、宣言オペランドとして、および代入ステートメントで特別な意味を持ちます。

事前宣言された識別子

以下の識別子は、ユニバースブロックに暗黙的に宣言されます [Go 1.18] [Go 1.21]。

Types:
	any bool byte comparable
	complex64 complex128 error float32 float64
	int int8 int16 int32 int64 rune string
	uint uint8 uint16 uint32 uint64 uintptr

Constants:
	true false iota

Zero value:
	nil

Functions:
	append cap clear close complex copy delete imag len
	make max min new panic print println real recover

エクスポートされた識別子

識別子は、別のパッケージからアクセスできるようにエクスポートされることがあります。識別子がエクスポートされるのは、次の両方に当てはまる場合です。

  1. 識別子の名前の最初の文字がUnicode大文字(Unicode文字カテゴリLu)であること。
  2. 識別子がパッケージブロックで宣言されているか、フィールド名またはメソッド名であること。

その他のすべての識別子はエクスポートされません。

識別子の一意性

識別子のセットが与えられた場合、識別子がそのセット内の他のすべての識別子と異なる場合、その識別子は一意であると呼ばれます。2つの識別子は、スペルが異なる場合、または異なるパッケージに属しエクスポートされていない場合に異なります。それ以外の場合は同じです。

定数宣言

定数宣言は、識別子のリスト(定数の名前)を定数式のリストの値にバインドします。識別子の数は式の数と等しくなければならず、左側のn番目の識別子は右側のn番目の式の値にバインドされます。

ConstDecl      = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
ConstSpec      = IdentifierList [ [ Type ] "=" ExpressionList ] .

IdentifierList = identifier { "," identifier } .
ExpressionList = Expression { "," Expression } .

型が存在する場合、すべての定数は指定された型を取り、式はその型に代入可能でなければなりません。その型は型パラメータであってはなりません。型が省略された場合、定数は対応する式の個々の型を取ります。式の値が型なしの定数である場合、宣言された定数は型なしのままであり、定数識別子は定数値を表します。例えば、式が浮動小数点リテラルである場合、リテラルの小数部がゼロであっても、定数識別子は浮動小数点定数を表します。

const Pi float64 = 3.14159265358979323846
const zero = 0.0         // untyped floating-point constant
const (
	size int64 = 1024
	eof        = -1  // untyped integer constant
)
const a, b, c = 3, 4, "foo"  // a = 3, b = 4, c = "foo", untyped integer and string constants
const u, v float32 = 0, 3    // u = 0.0, v = 3.0

括弧で囲まれたconst宣言リスト内では、最初のConstSpecを除き、式リストを省略できます。このような空のリストは、直前の空でない式リストとその型(もしあれば)のテキスト置換と同等です。したがって、式リストを省略することは、前のリストを繰り返すことと同等です。識別子の数は、前のリストの式の数と等しくなければなりません。iota定数ジェネレーターと合わせて、このメカニズムはシーケンシャルな値の軽量な宣言を可能にします。

const (
	Sunday = iota
	Monday
	Tuesday
	Wednesday
	Thursday
	Friday
	Partyday
	numberOfDays  // this constant is not exported
)

Iota

定数宣言内では、事前宣言された識別子iotaは連続する型なしの整数定数を表します。その値は、その定数宣言内の対応するConstSpecのインデックスであり、ゼロから始まります。これは関連する定数のセットを構築するために使用できます。

const (
	c0 = iota  // c0 == 0
	c1 = iota  // c1 == 1
	c2 = iota  // c2 == 2
)

const (
	a = 1 << iota  // a == 1  (iota == 0)
	b = 1 << iota  // b == 2  (iota == 1)
	c = 3          // c == 3  (iota == 2, unused)
	d = 1 << iota  // d == 8  (iota == 3)
)

const (
	u         = iota * 42  // u == 0     (untyped integer constant)
	v float64 = iota * 42  // v == 42.0  (float64 constant)
	w         = iota * 42  // w == 84    (untyped integer constant)
)

const x = iota  // x == 0
const y = iota  // y == 0

定義により、同じConstSpec内でiotaが複数回使用されても、すべて同じ値になります。

const (
	bit0, mask0 = 1 << iota, 1<<iota - 1  // bit0 == 1, mask0 == 0  (iota == 0)
	bit1, mask1                           // bit1 == 2, mask1 == 1  (iota == 1)
	_, _                                  //                        (iota == 2, unused)
	bit3, mask3                           // bit3 == 8, mask3 == 7  (iota == 3)
)

この最後の例は、最後の非空の式リストの暗黙の繰り返しを利用しています。

型宣言

型宣言は、識別子(型名)をにバインドします。型宣言には、エイリアス宣言と型定義の2つの形式があります。

TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
TypeSpec = AliasDecl | TypeDef .

エイリアス宣言

エイリアス宣言は、識別子を指定された型にバインドします[Go 1.9]。

AliasDecl = identifier [ TypeParameters ] "=" Type .

識別子のスコープ内では、それは与えられた型のエイリアスとして機能します。

type (
	nodeList = []*Node  // nodeList and []*Node are identical types
	Polar    = polar    // Polar and polar denote identical types
)

エイリアス宣言が型パラメータを指定する場合 [Go 1.24]、型名はジェネリックエイリアスを表します。ジェネリックエイリアスは使用時にインスタンス化されなければなりません。

type set[P comparable] = map[P]bool

エイリアス宣言において、与えられた型は型パラメータであってはなりません。

type A[P any] = P    // illegal: P is a type parameter

型定義

型定義は、与えられた型と同じ基底型と操作を持つ新しい、異なる型を作成し、それに識別子(型名)をバインドします。

TypeDef = identifier [ TypeParameters ] Type .

新しい型は定義された型と呼ばれます。これは、作成元の型を含む他のどの型とも異なります

type (
	Point struct{ x, y float64 }  // Point and struct{ x, y float64 } are different types
	polar Point                   // polar and Point denote different types
)

type TreeNode struct {
	left, right *TreeNode
	value any
}

type Block interface {
	BlockSize() int
	Encrypt(src, dst []byte)
	Decrypt(src, dst []byte)
}

定義された型には、それに関連付けられたメソッドがある場合があります。与えられた型にバインドされたメソッドは継承しませんが、インターフェース型または複合型の要素のメソッドセットは変更されません。

// A Mutex is a data type with two methods, Lock and Unlock.
type Mutex struct         { /* Mutex fields */ }
func (m *Mutex) Lock()    { /* Lock implementation */ }
func (m *Mutex) Unlock()  { /* Unlock implementation */ }

// NewMutex has the same composition as Mutex but its method set is empty.
type NewMutex Mutex

// The method set of PtrMutex's underlying type *Mutex remains unchanged,
// but the method set of PtrMutex is empty.
type PtrMutex *Mutex

// The method set of *PrintableMutex contains the methods
// Lock and Unlock bound to its embedded field Mutex.
type PrintableMutex struct {
	Mutex
}

// MyBlock is an interface type that has the same method set as Block.
type MyBlock Block

型定義は、異なるブール型、数値型、または文字列型を定義し、それらにメソッドを関連付けるために使用できます。

type TimeZone int

const (
	EST TimeZone = -(5 + iota)
	CST
	MST
	PST
)

func (tz TimeZone) String() string {
	return fmt.Sprintf("GMT%+dh", tz)
}

型定義が型パラメータを指定する場合、型名はジェネリック型を表します。ジェネリック型は使用時にインスタンス化されなければなりません。

type List[T any] struct {
	next  *List[T]
	value T
}

型定義において、与えられた型は型パラメータであってはなりません。

type T[P any] P    // illegal: P is a type parameter

func f[T any]() {
	type L T   // illegal: T is a type parameter declared by the enclosing function
}

ジェネリック型もまた、それに関連付けられたメソッドを持つことができます。この場合、メソッドのレシーバーは、ジェネリック型定義に存在するのと同じ数の型パラメータを宣言しなければなりません。

// The method Len returns the number of elements in the linked list l.
func (l *List[T]) Len() int  { … }

型パラメータ宣言

型パラメータリストは、ジェネリック関数または型宣言の型パラメータを宣言します。型パラメータリストは通常の関数パラメータリストのように見えますが、型パラメータ名はすべて存在しなければならず、リストは括弧ではなく角括弧で囲まれています [Go 1.18]。

TypeParameters = "[" TypeParamList [ "," ] "]" .
TypeParamList  = TypeParamDecl { "," TypeParamDecl } .
TypeParamDecl  = IdentifierList TypeConstraint .

リスト内の空白でないすべての名前は一意でなければなりません。各名前は型パラメータを宣言します。これは、宣言内で(まだ)不明な型のプレースホルダーとして機能する新しい異なる名前付き型です。型パラメータは、ジェネリック関数または型のインスタンス化時に型引数に置き換えられます。

[P any]
[S interface{ ~[]byte|string }]
[S ~[]E, E any]
[P Constraint[int]]
[_ any]

通常の関数パラメータがそれぞれパラメータ型を持つように、各型パラメータには対応する(メタ)型があり、これは型制約と呼ばれます。

ジェネリック型の型パラメータリストが、テキストP Cが有効な式を形成するような制約Cを持つ単一の型パラメータPを宣言する場合、解析の曖昧さが発生します。

type T[P *C] …
type T[P (C)] …
type T[P *C|Q] …
…

これらの稀なケースでは、型パラメータリストは式と区別できず、型宣言は配列型宣言として解析されます。この曖昧さを解決するには、制約をインターフェースに埋め込むか、末尾のカンマを使用します。

type T[P interface{*C}] …
type T[P *C,] …

型パラメータは、ジェネリック型に関連付けられたメソッド宣言のレシーバ仕様によっても宣言できます。

ジェネリック型Tの型パラメータリスト内で、型制約は(直接的または別のジェネリック型の型パラメータリストを介して間接的に)Tを参照してはなりません。

type T1[P T1[P]] …                    // illegal: T1 refers to itself
type T2[P interface{ T2[int] }] …     // illegal: T2 refers to itself
type T3[P interface{ m(T3[int])}] …   // illegal: T3 refers to itself
type T4[P T5[P]] …                    // illegal: T4 refers to T5 and
type T5[P T4[P]] …                    //          T5 refers to T4

type T6[P int] struct{ f *T6[P] }     // ok: reference to T6 is not in type parameter list

型制約

型制約は、対応する型パラメータに許容される型引数のセットを定義し、その型パラメータの値によってサポートされる操作を制御するインターフェースです [Go 1.18]。

TypeConstraint = TypeElem .

制約がinterface{E}の形式のインターフェースリテラルであり、Eが埋め込み型要素(メソッドではない)である場合、型パラメータリストでは便宜上、外側のinterface{ … }を省略できます。

[T []P]                      // = [T interface{[]P}]
[T ~int]                     // = [T interface{~int}]
[T int|string]               // = [T interface{int|string}]
type Constraint ~int         // illegal: ~int is not in a type parameter list

事前宣言されたインターフェース型comparableは、厳密に比較可能なすべての非インターフェース型のセットを表します [Go 1.18]。

型パラメータではないインターフェースは比較可能ですが、厳密に比較可能ではないため、comparableを実装しません。しかし、それらはcomparable満たします

int                          // implements comparable (int is strictly comparable)
[]byte                       // does not implement comparable (slices cannot be compared)
interface{}                  // does not implement comparable (see above)
interface{ ~int | ~string }  // type parameter only: implements comparable (int, string types are strictly comparable)
interface{ comparable }      // type parameter only: implements comparable (comparable implements itself)
interface{ ~int | ~[]byte }  // type parameter only: does not implement comparable (slices are not comparable)
interface{ ~struct{ any } }  // type parameter only: does not implement comparable (field any is not strictly comparable)

comparableインターフェースおよびcomparableを(直接的または間接的に)埋め込むインターフェースは、型制約としてのみ使用できます。それらは値や変数の型、または他の非インターフェース型のコンポーネントになることはできません。

型制約の充足

型引数Tが型制約C充足するのは、TCによって定義された型セットの要素である場合、つまりTC実装する場合です。例外として、厳密に比較可能な型制約は、比較可能な(必ずしも厳密に比較可能ではない)型引数によっても充足されることがあります [Go 1.20]。より正確には

型Tが制約C満たすのは、次のいずれかの条件が当てはまる場合です。

type argument      type constraint                // constraint satisfaction

int                interface{ ~int }              // satisfied: int implements interface{ ~int }
string             comparable                     // satisfied: string implements comparable (string is strictly comparable)
[]byte             comparable                     // not satisfied: slices are not comparable
any                interface{ comparable; int }   // not satisfied: any does not implement interface{ int }
any                comparable                     // satisfied: any is comparable and implements the basic interface any
struct{f any}      comparable                     // satisfied: struct{f any} is comparable and implements the basic interface any
any                interface{ comparable; m() }   // not satisfied: any does not implement the basic interface interface{ m() }
interface{ m() }   interface{ comparable; m() }   // satisfied: interface{ m() } is comparable and implements the basic interface interface{ m() }

制約充足ルールの例外のため、型パラメータ型のオペランドを比較すると、実行時にパニックが発生する可能性があります(比較可能な型パラメータは常に厳密に比較可能であるにもかかわらず)。

変数宣言

変数宣言は1つ以上の変数を作成し、対応する識別子をそれらにバインドし、それぞれに型と初期値を与えます。

VarDecl = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
var i int
var U, V, W float64
var k = 0
var x, y float32 = -1, -2
var (
	i       int
	u, v, s = 2.0, 3.0, "bar"
)
var re, im = complexSqrt(-1)
var _, found = entries[name]  // map lookup; only interested in "found"

式のリストが与えられた場合、変数は代入ステートメントの規則に従って式で初期化されます。それ以外の場合、各変数はそのゼロ値に初期化されます。

型が存在する場合、各変数にその型が与えられます。それ以外の場合、各変数には代入における対応する初期化値の型が与えられます。その値が型なし定数である場合、最初に暗黙的にそのデフォルト型変換されます。型なしのブール値である場合、最初に暗黙的にbool型に変換されます。事前宣言された識別子nilは、明示的な型のない変数を初期化するために使用できません。

var d = math.Sin(0.5)  // d is float64
var i = 42             // i is int
var t, ok = x.(T)      // t is T, ok is bool
var n = nil            // illegal

実装上の制約:コンパイラは、関数本体内で変数が一度も使用されない場合、その変数の宣言を不正とする場合があります。

短変数宣言

短変数宣言は以下の構文を使用します。

ShortVarDecl = IdentifierList ":=" ExpressionList .

これは、初期化式はあるが型がない通常の変数宣言の省略形です。

"var" IdentifierList "=" ExpressionList .
i, j := 0, 10
f := func() int { return 7 }
ch := make(chan int)
r, w, _ := os.Pipe()  // os.Pipe() returns a connected pair of Files and an error, if any
_, y, _ := coord(p)   // coord() returns three values; only interested in y coordinate

通常の変数宣言とは異なり、短変数宣言では、変数が元々同じブロック(またはブロックが関数本体である場合はパラメータリスト)で同じ型で宣言されており、かつ非空白の変数の少なくとも1つが新規である場合に、変数を再宣言できます。その結果、再宣言は多変数短宣言でのみ現れることができます。再宣言は新しい変数を導入しません。元の変数に新しい値を割り当てるだけです。:=の左側の非空白の変数名は一意でなければなりません。

field1, offset := nextField(str, 0)
field2, offset := nextField(str, offset)  // redeclares offset
x, y, x := 1, 2, 3                        // illegal: x repeated on left side of :=

短い変数宣言は、関数内でのみ使用できます。"if""for"、または"switch"ステートメントの初期化子のような一部のコンテキストでは、ローカルの一時変数を宣言するために使用できます。

関数宣言

関数宣言は、識別子である関数名を関数にバインドします。

FunctionDecl = "func" FunctionName [ TypeParameters ] Signature [ FunctionBody ] .
FunctionName = identifier .
FunctionBody = Block .

関数のシグネチャが結果パラメータを宣言する場合、関数本体のステートメントリストは終端ステートメントで終わる必要があります。

func IndexRune(s string, r rune) int {
	for i, c := range s {
		if c == r {
			return i
		}
	}
	// invalid: missing return statement
}

関数宣言が型パラメータを指定する場合、関数名はジェネリック関数を表します。ジェネリック関数は、呼び出すか値として使用する前にインスタンス化する必要があります。

func min[T ~int|~float64](x, y T) T {
	if x < y {
		return x
	}
	return y
}

型パラメータのない関数宣言は、本体を省略できます。このような宣言は、アセンブリルーチンなど、Goの外部で実装された関数のシグネチャを提供します。

func flushICache(begin, end uintptr)  // implemented externally

メソッド宣言

メソッドはレシーバーを持つ関数です。メソッド宣言は、識別子であるメソッド名をメソッドにバインドし、そのメソッドをレシーバーの基本型に関連付けます。

MethodDecl = "func" Receiver MethodName Signature [ FunctionBody ] .
Receiver   = Parameters .

レシーバーは、メソッド名の前に追加のパラメータセクションを介して指定されます。このパラメータセクションは、単一の非可変パラメータであるレシーバーを宣言する必要があります。その型は、定義されたT、または定義された型Tへのポインタであり、角括弧で囲まれた型パラメータ名[P1, P2, ...]のリストが続く場合があります。Tはレシーバー基本型と呼ばれます。レシーバー基本型はポインタ型またはインターフェース型にすることはできず、メソッドと同じパッケージで定義されている必要があります。メソッドは、そのレシーバー基本型にバインドされていると言われ、メソッド名は型Tまたは*Tセレクター内でのみ可視です。

ブランクレシーバー識別子は、メソッドシグネチャ内で一意である必要があります。メソッド本体内でレシーバーの値が参照されない場合、宣言でその識別子を省略できます。これは、一般に関数とメソッドのパラメータにも当てはまります。

基本型の場合、それにバインドされているメソッドの非ブランク名は一意である必要があります。基本型が構造体型の場合、非ブランクのメソッド名とフィールド名は明確である必要があります。

定義された型Pointが与えられた場合、宣言は

func (p *Point) Length() float64 {
	return math.Sqrt(p.x * p.x + p.y * p.y)
}

func (p *Point) Scale(factor float64) {
	p.x *= factor
	p.y *= factor
}

レシーバー型*Pointを持つメソッドLengthScaleを基本型Pointにバインドします。

レシーバーの基本型がジェネリック型である場合、レシーバーの指定は、メソッドが使用する対応する型パラメータを宣言する必要があります。これにより、レシーバーの型パラメータがメソッドで利用可能になります。構文的には、この型パラメータ宣言はレシーバーの基本型のインスタンス化のように見えます。型引数は、宣言されている型パラメータを表す識別子であり、レシーバーの基本型の各型パラメータに1つずつ存在する必要があります。型パラメータ名は、レシーバーの基本型定義内の対応するパラメータ名と一致する必要はなく、すべての非ブランクパラメータ名は、レシーバーパラメータセクションとメソッドシグネチャ内で一意である必要があります。レシーバーの型パラメータの制約は、レシーバーの基本型定義によって暗示されます。対応する型パラメータには対応する制約があります。

type Pair[A, B any] struct {
	a A
	b B
}

func (p Pair[A, B]) Swap() Pair[B, A]  { … }  // receiver declares A, B
func (p Pair[First, _]) First() First  { … }  // receiver declares First, corresponds to A in Pair

レシーバー型が(ポインタを介して)エイリアスによって示される場合、エイリアスはジェネリックであってはならず、他のエイリアスを介して直接的または間接的にインスタンス化されたジェネリック型を示してはならず、ポインタの参照解除に関係なく。

type GPoint[P any] = Point
type HPoint        = *GPoint[int]
type IPair         = Pair[int, int]

func (*GPoint[P]) Draw(P)   { … }  // illegal: alias must not be generic
func (HPoint) Draw(P)       { … }  // illegal: alias must not denote instantiated type GPoint[int]
func (*IPair) Second() int  { … }  // illegal: alias must not denote instantiated type Pair[int, int]

式は、演算子と関数をオペランドに適用することで値の計算を指定します。

オペランド

オペランドは、式の基本的な値を表します。オペランドは、リテラル、定数変数、または関数を表す(場合によっては修飾された)非ブランク識別子、または括弧で囲まれた式です。

Operand     = Literal | OperandName [ TypeArgs ] | "(" Expression ")" .
Literal     = BasicLit | CompositeLit | FunctionLit .
BasicLit    = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
OperandName = identifier | QualifiedIdent .

ジェネリック関数を表すオペランド名は、型引数のリストが続く場合があります。結果として得られるオペランドはインスタンス化された関数です。

ブランク識別子は、代入ステートメントの左辺でのみオペランドとして現れることができます。

実装制限:オペランドの型が空の型セットを持つ型パラメータである場合、コンパイラはエラーを報告する必要はありません。このような型パラメータを持つ関数はインスタンス化できません。試行すると、インスタンス化サイトでエラーが発生します。

修飾識別子

修飾識別子は、パッケージ名プレフィックスで修飾された識別子です。パッケージ名と識別子の両方をブランクにすることはできません。

QualifiedIdent = PackageName "." identifier .

修飾識別子は、異なるパッケージ内の識別子にアクセスします。そのパッケージはインポートされている必要があります。識別子はエクスポートされ、そのパッケージのパッケージブロックで宣言されている必要があります。

math.Sin // denotes the Sin function in package math

複合リテラル

複合リテラルは、評価されるたびに構造体、配列、スライス、マップの新しい値を構築します。これらは、リテラルの型と、ブレースで囲まれた要素のリストで構成されます。各要素には、オプションで対応するキーが先行する場合があります。

CompositeLit = LiteralType LiteralValue .
LiteralType  = StructType | ArrayType | "[" "..." "]" ElementType |
               SliceType | MapType | TypeName [ TypeArgs ] .
LiteralValue = "{" [ ElementList [ "," ] ] "}" .
ElementList  = KeyedElement { "," KeyedElement } .
KeyedElement = [ Key ":" ] Element .
Key          = FieldName | Expression | LiteralValue .
FieldName    = identifier .
Element      = Expression | LiteralValue .

LiteralTypeが型パラメータでない限り、その基底型は構造体、配列、スライス、またはマップ型でなければなりません(構文はこの制約をTypeNameとして型が与えられない限り強制します)。LiteralTypeが型パラメータである場合、その型セット内のすべての型は同じ基底型を持たなければならず、それは有効な複合リテラル型でなければなりません。要素とキーの型は、型Tの対応するフィールド、要素、およびキーの型に代入可能でなければなりません。追加の変換はありません。キーは、構造体リテラルのフィールド名、配列とスライスリテラルのインデックス、マップリテラルのキーとして解釈されます。マップリテラルの場合、すべての要素はキーを持たなければなりません。同じフィールド名または定数キー値を持つ複数の要素を指定するとエラーになります。非定数マップキーについては、評価順序のセクションを参照してください。

構造体リテラルには、次のルールが適用されます

  • キーは、構造体型で宣言されたフィールド名である必要があります。
  • キーを含まない要素リストは、フィールドが宣言された順序で各構造体フィールドの要素をリストする必要があります。
  • いずれかの要素がキーを持っている場合、すべての要素がキーを持っている必要があります。
  • キーを含む要素リストは、各構造体フィールドの要素を持つ必要はありません。省略されたフィールドは、そのフィールドのゼロ値を取得します。
  • リテラルは要素リストを省略できます。そのようなリテラルは、その型のゼロ値に評価されます。
  • 異なるパッケージに属する構造体の非エクスポートフィールドに要素を指定するとエラーになります。

宣言が与えられた場合

type Point3D struct { x, y, z float64 }
type Line struct { p, q Point3D }

書き込めます

origin := Point3D{}                            // zero value for Point3D
line := Line{origin, Point3D{y: -4, z: 12.3}}  // zero value for line.q.x

配列とスライスリテラルには、次のルールが適用されます

  • 各要素には、配列内での位置を示す関連する整数インデックスがあります。
  • キーを持つ要素は、キーをインデックスとして使用します。キーは、int型の値によって表現可能な非負の定数である必要があり、型付けされている場合は整数型である必要があります。
  • キーのない要素は、前の要素のインデックスに1を加えたものを使用します。最初の要素にキーがない場合、そのインデックスはゼロです。

複合リテラルのアドレスを取ると、リテラルの値で初期化された一意の変数へのポインタが生成されます。

var pointer *Point3D = &Point3D{y: 1000}

スライスまたはマップ型のゼロ値は、初期化されているが空の同じ型の値と同じではありません。したがって、空のスライスまたはマップ複合リテラルのアドレスを取っても、newで新しいスライスまたはマップ値を割り当てるのと同じ効果はありません。

p1 := &[]int{}    // p1 points to an initialized, empty slice with value []int{} and length 0
p2 := new([]int)  // p2 points to an uninitialized slice with value nil and length 0

配列リテラルの長さは、リテラル型で指定された長さです。リテラルで指定された要素の数が長さよりも少ない場合、不足している要素は配列要素型のゼロ値に設定されます。配列のインデックス範囲外のインデックス値を持つ要素を提供するとエラーになります。表記...は、最大要素インデックスに1を加えたものと同じ配列長を指定します。

buffer := [10]string{}             // len(buffer) == 10
intSet := [6]int{1, 2, 3, 5}       // len(intSet) == 6
days := [...]string{"Sat", "Sun"}  // len(days) == 2

スライスリテラルは、基になる配列リテラル全体を記述します。したがって、スライスリテラルの長さと容量は、最大要素インデックスに1を加えたものです。スライスリテラルは次の形式をとります

[]T{x1, x2, … xn}

そして、配列に適用されるスライス操作の省略形です

tmp := [n]T{x1, x2, … xn}
tmp[0 : n]

配列、スライス、またはマップ型Tの複合リテラル内で、それ自体が複合リテラルである要素またはマップキーは、それがTの要素型またはキー型と同一である場合、それぞれのリテラル型を省略できます。同様に、複合リテラルのアドレスである要素またはキーは、要素またはキーの型が*Tの場合、&Tを省略できます。

[...]Point{{1.5, -3.5}, {0, 0}}     // same as [...]Point{Point{1.5, -3.5}, Point{0, 0}}
[][]int{{1, 2, 3}, {4, 5}}          // same as [][]int{[]int{1, 2, 3}, []int{4, 5}}
[][]Point{{{0, 1}, {1, 2}}}         // same as [][]Point{[]Point{Point{0, 1}, Point{1, 2}}}
map[string]Point{"orig": {0, 0}}    // same as map[string]Point{"orig": Point{0, 0}}
map[Point]string{{0, 0}: "orig"}    // same as map[Point]string{Point{0, 0}: "orig"}

type PPoint *Point
[2]*Point{{1.5, -3.5}, {}}          // same as [2]*Point{&Point{1.5, -3.5}, &Point{}}
[2]PPoint{{1.5, -3.5}, {}}          // same as [2]PPoint{PPoint(&Point{1.5, -3.5}), PPoint(&Point{})}

LiteralTypeのTypeName形式を使用する複合リテラルが、"if"、"for"、または"switch"ステートメントのキーワードとブロックの開始ブレースの間のオペランドとして現れ、複合リテラルが括弧、角括弧、または中括弧で囲まれていない場合、解析の曖昧さが生じます。このまれなケースでは、リテラルの開始ブレースがステートメントのブロックを導入するものとして誤って解析されます。この曖昧さを解決するには、複合リテラルは括弧内に出現する必要があります。

if x == (T{a,b,c}[i]) { … }
if (x == T{a,b,c}[i]) { … }

有効な配列、スライス、マップリテラルの例

// list of prime numbers
primes := []int{2, 3, 5, 7, 9, 2147483647}

// vowels[ch] is true if ch is a vowel
vowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}

// the array [10]float32{-1, 0, 0, 0, -0.1, -0.1, 0, 0, 0, -1}
filter := [10]float32{-1, 4: -0.1, -0.1, 9: -1}

// frequencies in Hz for equal-tempered scale (A4 = 440Hz)
noteFrequency := map[string]float32{
	"C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83,
	"G0": 24.50, "A0": 27.50, "B0": 30.87,
}

関数リテラル

関数リテラルは匿名関数を表します。関数リテラルは型パラメータを宣言できません。

FunctionLit = "func" Signature FunctionBody .
func(a, b int, z float64) bool { return a*b < int(z) }

関数リテラルは変数に代入することも、直接呼び出すこともできます。

f := func(x, y int) int { return x + y }
func(ch chan int) { ch <- ACK }(replyChan)

関数リテラルはクロージャです。これらは周囲の関数で定義された変数を参照できます。これらの変数は周囲の関数と関数リテラルの間で共有され、アクセス可能な限り存続します。

プライマリ式

プライマリ式は、単項式と二項式のオペランドです。

PrimaryExpr   = Operand |
                Conversion |
                MethodExpr |
                PrimaryExpr Selector |
                PrimaryExpr Index |
                PrimaryExpr Slice |
                PrimaryExpr TypeAssertion |
                PrimaryExpr Arguments .

Selector      = "." identifier .
Index         = "[" Expression [ "," ] "]" .
Slice         = "[" [ Expression ] ":" [ Expression ] "]" |
                "[" [ Expression ] ":" Expression ":" Expression "]" .
TypeAssertion = "." "(" Type ")" .
Arguments     = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
x
2
(s + ".txt")
f(3.1415, true)
Point{1, 2}
m["foo"]
s[i : j + 1]
obj.color
f.p[i].x()

セレクター

パッケージ名ではないプライマリ式xの場合、セレクター式

x.f

xのフィールドまたはメソッドf(または場合によっては*x。下記参照)を表します。識別子fは(フィールドまたはメソッド)セレクターと呼ばれ、ブランク識別子であってはなりません。セレクター式の型はfの型です。xがパッケージ名の場合、修飾識別子のセクションを参照してください。

セレクターfは、型Tのフィールドまたはメソッドfを表すことも、Tのネストされた埋め込みフィールドのフィールドまたはメソッドfを参照することもできます。fに到達するために横断された埋め込みフィールドの数は、Tにおけるその深さと呼ばれます。Tで宣言されたフィールドまたはメソッドfの深さはゼロです。Tの埋め込みフィールドAで宣言されたフィールドまたはメソッドfの深さは、Aにおけるfの深さに1を加えたものです。

セレクターには次のルールが適用されます

  1. Tがポインタ型またはインターフェース型ではない型Tまたは*Tの値xの場合、x.fは、T内でそのようなfが存在する最も浅い深さのフィールドまたはメソッドを表します。最も浅い深さに正確に1つのfがない場合、セレクター式は不正です。
  2. Iがインターフェース型である型Iの値xの場合、x.fは、xの動的な値の、名前fを持つ実際のメソッドを表します。メソッドセットI内に名前fを持つメソッドがない場合、セレクター式は不正です。
  3. 例外として、xの型が定義されたポインタ型であり、(*x).fがフィールド(メソッドではない)を表す有効なセレクター式である場合、x.f(*x).fの省略形です。
  4. その他のすべてのケースでは、x.fは不正です。
  5. xがポインタ型で値がnilであり、x.fが構造体フィールドを表す場合、x.fへの代入または評価はランタイムパニックを引き起こします。
  6. xがインターフェース型で値がnilの場合、メソッドx.f呼び出しまたは評価ランタイムパニックを引き起こします。

たとえば、宣言が与えられた場合

type T0 struct {
	x int
}

func (*T0) M0()

type T1 struct {
	y int
}

func (T1) M1()

type T2 struct {
	z int
	T1
	*T0
}

func (*T2) M2()

type Q *T2

var t T2     // with t.T0 != nil
var p *T2    // with p != nil and (*p).T0 != nil
var q Q = p

書き込めます

t.z          // t.z
t.y          // t.T1.y
t.x          // (*t.T0).x

p.z          // (*p).z
p.y          // (*p).T1.y
p.x          // (*(*p).T0).x

q.x          // (*(*q).T0).x        (*q).x is a valid field selector

p.M0()       // ((*p).T0).M0()      M0 expects *T0 receiver
p.M1()       // ((*p).T1).M1()      M1 expects T1 receiver
p.M2()       // p.M2()              M2 expects *T2 receiver
t.M2()       // (&t).M2()           M2 expects *T2 receiver, see section on Calls

しかし、以下は無効です

q.M0()       // (*q).M0 is valid but not a field selector

メソッド式

Mが型Tメソッドセットにある場合、T.Mは、メソッドのレシーバーである追加の引数をプレフィックスとして持つMと同じ引数で通常の関数として呼び出し可能な関数です。

MethodExpr   = ReceiverType "." MethodName .
ReceiverType = Type .

レシーバーが型TであるMvと、レシーバーが型*TであるMpという2つのメソッドを持つ構造体型Tを考えてみます。

type T struct {
	a int
}
func (tv  T) Mv(a int) int         { return 0 }  // value receiver
func (tp *T) Mp(f float32) float32 { return 1 }  // pointer receiver

var t T

T.Mv

Mvと同等の関数を生成しますが、最初の引数として明示的なレシーバーを持ちます。そのシグネチャは次のとおりです。

func(tv T, a int) int

その関数は明示的なレシーバーで通常通り呼び出すことができるため、これら5つの呼び出しは同等です

t.Mv(7)
T.Mv(t, 7)
(T).Mv(t, 7)
f1 := T.Mv; f1(t, 7)
f2 := (T).Mv; f2(t, 7)

同様に、式

(*T).Mp

シグネチャを持つMpを表す関数値を生成します

func(tp *T, f float32) float32

値レシーバーを持つメソッドの場合、明示的なポインターレシーバーを持つ関数を派生させることができます。

(*T).Mv

シグネチャを持つMvを表す関数値を生成します

func(tv *T, a int) int

そのような関数は、レシーバーを間接参照して、基になるメソッドにレシーバーとして渡す値を作成します。このメソッドは、関数呼び出しで渡されたアドレスの値は上書きしません。

最後のケース、ポインターレシーバーメソッドの値レシーバー関数は、ポインターレシーバーメソッドが値型のメソッドセットに含まれないため不正です。

メソッドから派生した関数値は、関数呼び出し構文で呼び出されます。レシーバーは、呼び出しの最初の引数として提供されます。つまり、f := T.Mvが与えられた場合、ft.f(7)ではなくf(t, 7)として呼び出されます。レシーバーをバインドする関数を構築するには、関数リテラルまたはメソッド値を使用します。

インターフェース型のメソッドから関数値を派生させることは合法です。結果として得られる関数は、そのインターフェース型の明示的なレシーバーを受け取ります。

メソッド値

xが静的型Tを持ち、Mが型Tメソッドセットにある場合、x.Mメソッド値と呼ばれます。メソッド値x.Mは、x.Mのメソッド呼び出しと同じ引数で呼び出し可能な関数値です。式xはメソッド値の評価中に評価および保存されます。保存されたコピーは、後で実行される可能性のあるすべての呼び出しでレシーバーとして使用されます。

type S struct { *T }
type T int
func (t T) M() { print(t) }

t := new(T)
s := S{T: t}
f := t.M                    // receiver *t is evaluated and stored in f
g := s.M                    // receiver *(s.T) is evaluated and stored in g
*t = 42                     // does not affect stored receivers in f and g

Tはインターフェース型または非インターフェース型である可能性があります。

上記のメソッド式の議論と同様に、レシーバーが型TであるMvと、レシーバーが型*TであるMpという2つのメソッドを持つ構造体型Tを考えてみます。

type T struct {
	a int
}
func (tv  T) Mv(a int) int         { return 0 }  // value receiver
func (tp *T) Mp(f float32) float32 { return 1 }  // pointer receiver

var t T
var pt *T
func makeT() T

t.Mv

型の関数値を生成します

func(int) int

これらの2つの呼び出しは同等です

t.Mv(7)
f := t.Mv; f(7)

同様に、式

pt.Mp

型の関数値を生成します

func(float32) float32

セレクターと同様に、ポインタを使用した値レシーバーを持つ非インターフェースメソッドへの参照は、そのポインタを自動的に逆参照します。pt.Mv(*pt).Mvと同等です。

メソッド呼び出しと同様に、アドレス可能な値を使用したポインターレシーバーを持つ非インターフェースメソッドへの参照は、その値のアドレスを自動的に取得します。t.Mp(&t).Mpと同等です。

f := t.Mv; f(7)   // like t.Mv(7)
f := pt.Mp; f(7)  // like pt.Mp(7)
f := pt.Mv; f(7)  // like (*pt).Mv(7)
f := t.Mp; f(7)   // like (&t).Mp(7)
f := makeT().Mp   // invalid: result of makeT() is not addressable

上記の例では非インターフェース型を使用していますが、インターフェース型の値からメソッド値を作成することも合法です。

var i interface { M(int) } = myVal
f := i.M; f(7)  // like i.M(7)

インデックス式

次の形式のプライマリ式

a[x]

配列、配列へのポインタ、スライス、文字列、またはマップaの、xでインデックス付けされた要素を表します。値xはそれぞれインデックスまたはマップキーと呼ばれます。次のルールが適用されます

aがマップでも型パラメータでもない場合

  • インデックスxは型なし定数であるか、その型が整数であるか、型セットに整数型のみが含まれる型パラメータである必要があります。
  • 定数インデックスは非負であり、int型の値によって表現可能である必要があります。整数型である必要があります。
  • 型なしの定数インデックスはint型になります。
  • インデックスxは、0 <= x < len(a)の場合範囲内であり、それ以外の場合は範囲外です。

配列型Aaの場合

  • 定数インデックスは範囲内である必要があります。
  • ランタイムにxが範囲外の場合、ランタイムパニックが発生します。
  • a[x]はインデックスxの配列要素であり、a[x]の型はAの要素型です。

配列型へのポインタaの場合

  • a[x](*a)[x]の省略形です。

スライス型Saの場合

  • ランタイムにxが範囲外の場合、ランタイムパニックが発生します。
  • a[x]はインデックスxのスライス要素であり、a[x]の型はSの要素型です。

文字列型aの場合

  • 文字列aも定数である場合、定数インデックスは範囲内である必要があります。
  • ランタイムにxが範囲外の場合、ランタイムパニックが発生します。
  • a[x]はインデックスxの非定数バイト値であり、a[x]の型はbyteです。
  • a[x]には代入できません。

マップ型Maの場合

  • xの型はMのキー型に代入可能である必要があります。
  • マップにキーxを持つエントリが含まれている場合、a[x]はキーxを持つマップ要素であり、a[x]の型はMの要素型です。
  • マップがnilであるか、そのようなエントリが含まれていない場合、a[x]Mの要素型のゼロ値です。

型パラメータ型Paの場合

  • インデックス式a[x]は、Pの型セット内のすべての型の値に対して有効でなければなりません。
  • Pの型セット内のすべての型の要素型は同一でなければなりません。この文脈では、文字列型の要素型はbyteです。
  • Pの型セットにマップ型がある場合、その型セット内のすべての型はマップ型でなければならず、それぞれのキー型はすべて同一でなければなりません。
  • a[x]は、Pがインスタンス化される型引数のインデックスxにおける配列、スライス、または文字列要素、あるいはキーxを持つマップ要素であり、a[x]の型は(同一の)要素型の型です。
  • Pの型セットに文字列型が含まれる場合、a[x]には代入できません。

それ以外の場合、a[x]は不正です。

特殊な形式の代入ステートメントまたは初期化で使用される、型map[K]Vのマップaに対するインデックス式

v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]

追加の型なしブール値を生成します。okの値は、キーxがマップに存在する場合はtrue、それ以外の場合はfalseです。

nilマップの要素に代入すると、ランタイムパニックが発生します。

スライス式

スライス式は、文字列、配列、配列へのポインタ、またはスライスオペランドから部分文字列またはスライスを構築します。2つのバリアントがあります。下限と上限を指定する単純な形式と、容量の上限も指定する完全な形式です。

オペランド型が型パラメータである場合、その型セットに文字列型が含まれない限り、型セット内のすべての型は同じ基底型を持たなければならず、スライス式はその型のオペランドに対して有効でなければなりません。型セットに文字列型が含まれる場合、基底型[]byteを持つバイトスライスも含まれる場合があります。この場合、スライス式はstring型のオペランドに対して有効でなければなりません。

単純スライス式

文字列、配列、配列へのポインタ、またはスライスaの場合、プライマリ式

a[low : high]

部分文字列またはスライスを構築します。インデックス lowhighは、オペランドaのどの要素が結果に現れるかを選択します。結果は0から始まるインデックスを持ち、長さはhigh - lowに等しくなります。配列aをスライスした後

a := [5]int{1, 2, 3, 4, 5}
s := a[1:4]

スライスsは型[]int、長さ3、容量4、および要素を持ちます

s[0] == 2
s[1] == 3
s[2] == 4

便宜上、インデックスのいずれかを省略できます。欠落したlowインデックスはゼロにデフォルト設定されます。欠落したhighインデックスはスライスされたオペランドの長さにデフォルト設定されます。

a[2:]  // same as a[2 : len(a)]
a[:3]  // same as a[0 : 3]
a[:]   // same as a[0 : len(a)]

aが配列へのポインタの場合、a[low : high](*a)[low : high]の省略形です。

配列または文字列の場合、インデックスは0 <= low <= high <= len(a)の場合範囲内であり、それ以外の場合は範囲外です。スライスの場合、上限インデックスは長さではなくスライス容量cap(a)です。定数インデックスは非負であり、int型の値によって表現可能である必要があります。配列または定数文字列の場合、定数インデックスも範囲内である必要があります。両方のインデックスが定数である場合、low <= highを満たす必要があります。ランタイムにインデックスが範囲外の場合、ランタイムパニックが発生します。

型なし文字列を除き、スライスされたオペランドが文字列またはスライスの場合、スライス操作の結果はオペランドと同じ型の非定数値です。型なし文字列オペランドの場合、結果はstring型の非定数値です。スライスされたオペランドが配列の場合、それはアドレス可能でなければならず、スライス操作の結果は配列と同じ要素型を持つスライスです。

有効なスライス式のスライスされたオペランドがnilスライスの場合、結果はnilスライスです。それ以外の場合、結果がスライスであれば、オペランドと基になる配列を共有します。

var a [10]int
s1 := a[3:7]   // underlying array of s1 is array a; &s1[2] == &a[5]
s2 := s1[1:4]  // underlying array of s2 is underlying array of s1 which is array a; &s2[1] == &a[5]
s2[1] = 42     // s2[1] == s1[2] == a[5] == 42; they all refer to the same underlying array element

var s []int
s3 := s[:0]    // s3 == nil

完全スライス式

配列、配列へのポインタ、またはスライスa(文字列ではない)の場合、プライマリ式

a[low : high : max]

単純なスライス式a[low : high]と同じ型、長さ、要素を持つスライスを構築します。さらに、結果のスライスの容量をmax - lowに設定することで制御します。最初のインデックスのみを省略できます。デフォルトは0です。配列aをスライスした後

a := [5]int{1, 2, 3, 4, 5}
t := a[1:3:5]

スライスtは型[]int、長さ2、容量4、および要素を持ちます

t[0] == 2
t[1] == 3

単純スライス式と同様に、aが配列へのポインタの場合、a[low : high : max](*a)[low : high : max]の省略形です。スライスされたオペランドが配列の場合、それはアドレス可能でなければなりません。

インデックスは0 <= low <= high <= max <= cap(a)の場合範囲内であり、それ以外の場合は範囲外です。定数インデックスは非負であり、int型の値によって表現可能である必要があります。配列の場合、定数インデックスも範囲内である必要があります。複数のインデックスが定数である場合、存在する定数は互いに対して範囲内である必要があります。ランタイムにインデックスが範囲外の場合、ランタイムパニックが発生します。

型アサーション

インターフェース型の式x(ただし型パラメータではない)と型Tの場合、プライマリ式

x.(T)

xnilではなく、xに格納されている値が型Tであるとアサートします。表記x.(T)型アサーションと呼ばれます。

より正確には、Tがインターフェース型でない場合、x.(T)xの動的型が型T同一であるとアサートします。この場合、Txの(インターフェース)型を実装しなければなりません。そうでない場合、xが型Tの値を格納することは不可能であるため、型アサーションは無効です。Tがインターフェース型である場合、x.(T)xの動的型がインターフェースT実装するとアサートします。

型アサーションが成り立つ場合、式の値はxに格納されている値であり、その型はTです。型アサーションが偽の場合、ランタイムパニックが発生します。言い換えれば、xの動的型はランタイムにしかわからないものの、正しいプログラムではx.(T)の型はTであることがわかっています。

var x interface{} = 7          // x has dynamic type int and value 7
i := x.(int)                   // i has type int and value 7

type I interface { m() }

func f(y I) {
	s := y.(string)        // illegal: string does not implement I (missing method m)
	r := y.(io.Reader)     // r has type io.Reader and the dynamic type of y must implement both I and io.Reader
	…
}

特殊な形式の代入ステートメントまたは初期化で使用される型アサーション

v, ok = x.(T)
v, ok := x.(T)
var v, ok = x.(T)
var v, ok interface{} = x.(T) // dynamic types of v and ok are T and bool

追加の型なしブール値を生成します。okの値は、アサーションが成り立つ場合はtrueです。それ以外の場合はfalseであり、vの値は型Tゼロ値です。この場合、ランタイムパニックは発生しません。

呼び出し

関数型Fの式fが与えられた場合、

f(a1, a2, … an)

引数a1, a2, … anfを呼び出します。1つの特殊なケースを除き、引数はFのパラメータ型に代入可能な単一値式であり、関数が呼び出される前に評価されます。式の型はFの結果型です。メソッド呼び出しも同様ですが、メソッド自体は、そのメソッドのレシーバー型の値に対するセレクターとして指定されます。

math.Atan2(x, y)  // function call
var pt *Point
pt.Scale(3.5)     // method call with receiver pt

fがジェネリック関数を表す場合、呼び出すか関数値として使用する前にインスタンス化する必要があります。

fの型が型パラメータである場合、その型セット内のすべての型は、関数型である同じ基底型を持たなければならず、関数呼び出しはその型に対して有効でなければなりません。

関数呼び出しでは、関数値と引数は通常の順序で評価されます。評価後、関数の変数(パラメータと結果を含む)のために新しいストレージが割り当てられます。次に、呼び出しの引数が関数に渡されます。これは、それらが対応する関数パラメータに代入され、呼び出された関数が実行を開始することを意味します。関数の戻りパラメータは、関数が戻るときに呼び出し元に返されます。

nil関数値を呼び出すと、ランタイムパニックが発生します。

特殊なケースとして、関数またはメソッドgの戻り値が別の関数またはメソッドfのパラメータと数的に等しく、個々に代入可能である場合、呼び出しf(g(parameters_of_g))は、gの戻り値をfのパラメータに順に渡した後でfを呼び出します。fの呼び出しは、gの呼び出し以外のパラメータを含んではならず、gは少なくとも1つの戻り値を持たなければなりません。fが最後の...パラメータを持つ場合、それは通常のパラメータの代入後に残るgの戻り値に代入されます。

func Split(s string, pos int) (string, string) {
	return s[0:pos], s[pos:]
}

func Join(s, t string) string {
	return s + t
}

if Join(Split(value, len(value)/2)) != value {
	log.Panic("test fails")
}

メソッド呼び出しx.m()は、(xの型の)メソッドセットmを含み、引数リストがmのパラメータリストに代入可能である場合に有効です。xアドレス可能であり、&xのメソッドセットがmを含む場合、x.m()(&x).m()の省略形です。

var p Point
p.Scale(3.5)

distinctなメソッド型はなく、メソッドリテラルもありません。

...パラメータへの引数渡し

fが型...Tの最後のパラメータpを持つ可変長引数である場合、f内ではpの型は型[]Tと同等です。fpに対する実際の引数なしで呼び出された場合、p渡される値はnilです。それ以外の場合、渡される値は、新しい基になる配列を持つ新しい型[]Tのスライスであり、その連続する要素は実際の引数です。これらの引数はすべてT代入可能でなければなりません。したがって、スライスの長さと容量はpにバインドされた引数の数であり、呼び出しサイトごとに異なる場合があります。

関数と呼び出しが与えられた場合

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")

Greeting内では、最初の呼び出しではwhonil値を持ち、2番目の呼び出しでは[]string{"Joe", "Anna", "Eileen"}となります。

最後の引数がスライス型[]Tに代入可能で、その後に...が続く場合、それは...Tパラメータの値として変更されずに渡されます。この場合、新しいスライスは作成されません。

スライスsと呼び出しが与えられた場合

s := []string{"James", "Jasmine"}
Greeting("goodbye:", s...)

Greeting内では、whosと同じ値を持ち、基になる配列も同じです。

インスタンス化

ジェネリック関数または型は、型パラメータに型引数を置き換えることによってインスタンス化されます[Go 1.18]。インスタンス化は2つのステップで進行します。

  1. 各型引数は、ジェネリック宣言内の対応する型パラメータに置き換えられます。この置換は、型パラメータリスト自体とそのリスト内のすべての型を含む、関数または型宣言全体で発生します。
  2. 置換後、各型引数は、対応する型パラメータの制約(必要に応じてインスタンス化される)を満たす必要があります。そうでない場合、インスタンス化は失敗します。

型をインスタンス化すると、新しい非ジェネリックな名前付き型が生成されます。関数をインスタンス化すると、新しい非ジェネリックな関数が生成されます。

type parameter list    type arguments    after substitution

[P any]                int               int satisfies any
[S ~[]E, E any]        []int, int        []int satisfies ~[]int, int satisfies any
[P io.Writer]          string            illegal: string doesn't satisfy io.Writer
[P comparable]         any               any satisfies (but does not implement) comparable

ジェネリック関数を使用する場合、型引数は明示的に指定することも、関数が使用されるコンテキストから部分的または完全に推論することもできます。推論可能であれば、関数が次のいずれかの場合、型引数リストは完全に省略できます。

他のすべての場合、(部分的である可能性のある) 型引数リストが存在しなければなりません。型引数リストが存在しないか部分的である場合、欠落しているすべての型引数は、関数が使用されるコンテキストから推論可能でなければなりません。

// sum returns the sum (concatenation, for strings) of its arguments.
func sum[T ~int | ~float64 | ~string](x... T) T { … }

x := sum                       // illegal: the type of x is unknown
intSum := sum[int]             // intSum has type func(x... int) int
a := intSum(2, 3)              // a has value 5 of type int
b := sum[float64](2.0, 3)      // b has value 5.0 of type float64
c := sum(b, -1)                // c has value 4.0 of type float64

type sumFunc func(x... string) string
var f sumFunc = sum            // same as var f sumFunc = sum[string]
f = sum                        // same as f = sum[string]

部分的な型引数リストは空であってはなりません。少なくとも最初の引数は存在しなければなりません。このリストは、完全な型引数リストのプレフィックスであり、残りの引数は推論されることになります。大まかに言えば、型引数は「右から左へ」省略できます。

func apply[S ~[]E, E any](s S, f func(E) E) S { … }

f0 := apply[]                  // illegal: type argument list cannot be empty
f1 := apply[[]int]             // type argument for S explicitly provided, type argument for E inferred
f2 := apply[[]string, string]  // both type arguments explicitly provided

var bytes []byte
r := apply(bytes, func(byte) byte { … })  // both type arguments inferred from the function arguments

ジェネリック型の場合、すべての型引数は常に明示的に提供されなければなりません。

型推論

ジェネリック関数を使用する場合、関数の型パラメータの制約を含む、関数が使用されるコンテキストから型引数が推論可能であれば、一部またはすべての型引数を省略できます。型推論は、欠落している型引数を推論でき、推論された型引数でインスタンス化が成功した場合に成功します。そうでない場合、型推論は失敗し、プログラムは無効です。

型推論は、推論のために型ペア間の型関係を使用します。たとえば、関数引数はそれぞれの関数パラメータに代入可能でなければなりません。これにより、引数の型とパラメータの型との間に関係が確立されます。これらの2つの型のいずれかが型パラメータを含む場合、型推論は、代入可能性関係が満たされるように型パラメータを置き換える型引数を探します。同様に、型推論は、型引数がそれぞれの型パラメータの制約を満たす必要があるという事実を使用します。

そのような一致する型の各ペアは、1つまたは複数の型パラメータを含む型方程式に対応します。これは、1つまたは複数のジェネリック関数からのものである可能性があります。欠落している型引数を推論することは、それぞれの型パラメータに対して結果として得られる型方程式のセットを解くことを意味します。

例えば、与えられた

// dedup returns a copy of the argument slice with any duplicate entries removed.
func dedup[S ~[]E, E comparable](S) S { … }

type Slice []int
var s Slice
s = dedup(s)   // same as s = dedup[Slice, int](s)

プログラムが有効であるためには、型Sliceの変数sは関数パラメータ型Sに代入可能でなければなりません。複雑さを軽減するために、型推論は代入の方向性を無視するため、SliceSの間の型関係は、(対称的な)型方程式Slice ≡A S(または、そのことについてはS ≡A Slice)で表現できます。ここで、AAは、LHSとRHSの型が代入可能性ルールに従って一致しなければならないことを示します(詳細は型ユニフィケーションのセクションを参照)。同様に、型パラメータSはその制約~[]Eを満たさなければなりません。これは、X ≡C Yが「Xが制約Yを満たす」ことを意味するS ≡C ~[]Eとして表現できます。これらの観察により、2つの方程式のセットが導き出されます。

	Slice ≡A S      (1)
	S     ≡C ~[]E   (2)

これで型パラメータSEについて解くことができます。(1)から、コンパイラはSの型引数がSliceであることを推論できます。同様に、Sliceの基底型は[]intであり、[]intが制約の[]Eと一致しなければならないため、コンパイラはEintでなければならないと推論できます。したがって、これら2つの方程式に対して、型推論は次のように推論します。

	S ➞ Slice
	E ➞ int

型方程式のセットが与えられた場合、解決すべき型パラメータは、インスタンス化する必要があり、かつ明示的な型引数が提供されていない関数の型パラメータです。これらの型パラメータは、バインドされた型パラメータと呼ばれます。たとえば、上記のdedupの例では、型パラメータSEdedupにバインドされています。ジェネリック関数呼び出しの引数は、それ自体がジェネリック関数である場合があります。その関数の型パラメータは、バインドされた型パラメータのセットに含まれます。関数引数の型は、他の関数(関数呼び出しを囲むジェネリック関数など)からの型パラメータを含む場合があります。これらの型パラメータも型方程式に現れる場合がありますが、そのコンテキストではバインドされていません。型方程式は、常にバインドされた型パラメータのみについて解決されます。

型推論は、ジェネリック関数の呼び出しと、ジェネリック関数から(明示的に関数型を持つ)変数への代入をサポートします。これには、ジェネリック関数を他の(おそらくジェネリックな)関数に引数として渡したり、ジェネリック関数を結果として返したりすることが含まれます。型推論は、これらの各ケースに固有の式セットに基づいて動作します。式は次のとおりです(分かりやすくするために型引数リストは省略されています)。

  • fまたは関数引数aiがジェネリック関数である関数呼び出しf(a0, a1, …)の場合
    対応する関数引数とパラメータの各ペア(ai, pi)で、ai型なし定数でない場合、方程式typeof(pi) ≡A typeof(ai)が生成されます。
    aiが型なし定数cjであり、typeof(pi)がバインドされた型パラメータPkである場合、ペア(cj, Pk)は型方程式とは別に収集されます。

  • ジェネリック関数fを関数型の(非ジェネリックな)変数vに代入するv = fの場合
    typeof(v) ≡A typeof(f).

  • return …, f, … という戻りステートメントで、fが関数型の(非ジェネリックな)結果変数rに結果として返されるジェネリック関数である場合
    typeof(r) ≡A typeof(f).

さらに、各型パラメータPkとそれに対応する型制約Ckは、型方程式PkC Ckを生成します。

型推論は、型付けされたオペランドから取得された型情報を、型なし定数を考慮する前に優先します。したがって、推論は2つのフェーズで進行します。

  1. 型方程式は、型ユニフィケーションを使用してバインドされた型パラメータについて解決されます。ユニフィケーションが失敗した場合、型推論は失敗します。

  2. まだ型引数が推論されておらず、かつその同じ型パラメータを持つ1つ以上のペア(cj, Pk)が収集された各バインドされた型パラメータPkについて、定数式の場合と同様に、それらのすべてのペア内の定数cj定数種類を決定します。Pkの型引数は、決定された定数種類のデフォルト型です。定数種類の競合により定数種類を決定できない場合、型推論は失敗します。

これら2つのフェーズの後もすべての型引数が見つからない場合、型推論は失敗します。

両方のフェーズが成功した場合、型推論は各バインドされた型パラメータの型引数を決定しました。

	Pk ➞ Ak

型引数Akは、複合型である場合があり、要素型として他のバインドされた型パラメータPkを含む場合(あるいは単に別のバインドされた型パラメータである場合)があります。繰り返しの単純化の過程で、各型引数内のバインドされた型パラメータは、それらの型パラメータのそれぞれの型引数で置換され、各型引数がバインドされた型パラメータを含まなくなるまで続きます。

型引数がバインドされた型パラメータを介してそれら自体への循環参照を含む場合、単純化、したがって型推論は失敗します。それ以外の場合、型推論は成功します。

型ユニフィケーション

型推論は型ユニフィケーションを介して型方程式を解きます。型ユニフィケーションは、方程式のLHSとRHSの型を再帰的に比較し、どちらかまたは両方の型がバインドされた型パラメータであるか、またはそれらを含む場合があり、LHSとRHSが一致する(文脈に応じて同一または代入互換になる)ようにそれらの型パラメータの型引数を探します。その目的のために、型推論は、バインドされた型パラメータから推論された型引数へのマップを維持します。このマップは、型ユニフィケーション中に参照および更新されます。最初は、バインドされた型パラメータは既知ですが、マップは空です。型ユニフィケーション中に、新しい型引数Aが推論されると、型パラメータから引数へのそれぞれのマッピングP ➞ Aがマップに追加されます。逆に、型を比較するとき、既知の型引数(マップエントリがすでに存在する型引数)は、対応する型パラメータの代わりに配置されます。型推論が進むにつれて、すべての等式が考慮されるまで、またはユニフィケーションが失敗するまでマップはますます埋められていきます。型推論は、ユニフィケーションステップが失敗せず、マップに各型パラメータのエントリがある場合に成功します。

例えば、バインドされた型パラメータPを持つ型方程式が与えられた場合

	[10]struct{ elem P, list []P } ≡A [10]struct{ elem string; list []string }

型推論は空のマップから始まります。ユニフィケーションはまず、LHSとRHSの型のトップレベル構造を比較します。両方とも同じ長さの配列です。要素型がユニファイする場合にユニファイします。両方の要素型は構造体です。同じ名前の同じ数のフィールドを持ち、フィールド型がユニファイする場合にユニファイします。Pの型引数はまだ不明であるため(マップエントリがないため)、Pstringとユニファイすると、マッピングP ➞ stringがマップに追加されます。listフィールドの型をユニファイするには、[]P[]stringをユニファイする必要があり、したがってPstringをユニファイする必要があります。この時点でPの型引数は既知であるため(Pのマップエントリがあるため)、その型引数stringPの代わりに配置されます。そしてstringstringと同一であるため、このユニフィケーションステップも成功します。方程式のLHSとRHSのユニフィケーションはこれで完了しました。型推論は、型方程式が1つだけであり、ユニフィケーションステップが失敗せず、マップが完全に埋められているため成功します。

ユニフィケーションは、2つの型が同一であるか、代入互換であるか、または構造的に等しいかどうかに応じて、厳密なユニフィケーションと緩いユニフィケーションの組み合わせを使用します。それぞれの型ユニフィケーションルールは、付録に詳しく説明されています。

X ≡A Yの形式の方程式の場合、ここでXYは代入(パラメータ渡しとreturnステートメントを含む)に関与する型であり、トップレベルの型構造は緩く統一される場合がありますが、要素型は代入のルールに合わせて厳密に統一される必要があります。

P ≡C Cの形式の方程式の場合、ここでPは型パラメータであり、Cは対応する制約です。ユニフィケーションルールは少し複雑です。

  • Cの型セット内のすべての型が同じ基底型Uを持ち、Pが既知の型引数Aを持つ場合、UAは緩く統一されなければなりません。
  • 同様に、Cの型セット内のすべての型が同じ要素型と非競合するチャネル方向を持つチャネル型であり、Pが既知の型引数Aを持つ場合、Cの型セット内で最も制限の厳しいチャネル型とAは緩く統一されなければなりません。
  • Pが既知の型引数を持たず、Cが基底(チルダ)型ではない型項Tを正確に1つ含む場合、ユニフィケーションはマッピングP ➞ Tをマップに追加します。
  • Cが上記のような型Uを持たず、Pが既知の型引数Aを持つ場合、ACのすべてのメソッド(もしあれば)を持たなければならず、対応するメソッド型は厳密に統一されなければなりません。

型制約から型方程式を解くとき、1つの方程式を解くことで追加の型引数が推論される可能性があり、それらの型引数に依存する他の方程式を解くことが可能になります。型推論は、新しい型引数が推論される限り型ユニフィケーションを繰り返します。

演算子

演算子はオペランドを式に結合します。

Expression = UnaryExpr | Expression binary_op Expression .
UnaryExpr  = PrimaryExpr | unary_op UnaryExpr .

binary_op  = "||" | "&&" | rel_op | add_op | mul_op .
rel_op     = "==" | "!=" | "<" | "<=" | ">" | ">=" .
add_op     = "+" | "-" | "|" | "^" .
mul_op     = "*" | "/" | "%" | "<<" | ">>" | "&" | "&^" .

unary_op   = "+" | "-" | "!" | "^" | "*" | "&" | "<-" .

比較は別の場所で議論されています。他の二項演算子の場合、操作にシフトまたは型なし定数が関与しない限り、オペランドの型は同一である必要があります。定数のみが関与する操作については、定数式のセクションを参照してください。

シフト操作を除き、一方のオペランドが型なし定数であり、もう一方のオペランドがそうでない場合、定数は暗黙的に他のオペランドの型に変換されます。

シフト式の右オペランドは整数型[Go 1.13]であるか、uint型の値によって表現可能な型なし定数である必要があります。非定数シフト式の左オペランドが型なし定数である場合、シフト式がその左オペランドのみで置き換えられた場合に想定される型に最初に暗黙的に変換されます。

var a [1024]byte
var s uint = 33

// The results of the following examples are given for 64-bit ints.
var i = 1<<s                   // 1 has type int
var j int32 = 1<<s             // 1 has type int32; j == 0
var k = uint64(1<<s)           // 1 has type uint64; k == 1<<33
var m int = 1.0<<s             // 1.0 has type int; m == 1<<33
var n = 1.0<<s == j            // 1.0 has type int32; n == true
var o = 1<<s == 2<<s           // 1 and 2 have type int; o == false
var p = 1<<s == 1<<33          // 1 has type int; p == true
var u = 1.0<<s                 // illegal: 1.0 has type float64, cannot shift
var u1 = 1.0<<s != 0           // illegal: 1.0 has type float64, cannot shift
var u2 = 1<<s != 1.0           // illegal: 1 has type float64, cannot shift
var v1 float32 = 1<<s          // illegal: 1 has type float32, cannot shift
var v2 = string(1<<s)          // illegal: 1 is converted to a string, cannot shift
var w int64 = 1.0<<33          // 1.0<<33 is a constant shift expression; w == 1<<33
var x = a[1.0<<s]              // panics: 1.0 has type int, but 1<<33 overflows array bounds
var b = make([]byte, 1.0<<s)   // 1.0 has type int; len(b) == 1<<33

// The results of the following examples are given for 32-bit ints,
// which means the shifts will overflow.
var mm int = 1.0<<s            // 1.0 has type int; mm == 0
var oo = 1<<s == 2<<s          // 1 and 2 have type int; oo == true
var pp = 1<<s == 1<<33         // illegal: 1 has type int, but 1<<33 overflows int
var xx = a[1.0<<s]             // 1.0 has type int; xx == a[0]
var bb = make([]byte, 1.0<<s)  // 1.0 has type int; len(bb) == 0

演算子の優先順位

単項演算子は最高の優先順位を持ちます。++--演算子は式ではなくステートメントを形成するため、演算子の階層外にあります。結果として、ステートメント*p++(*p)++と同じです。

二項演算子には5つの優先順位レベルがあります。乗算演算子が最も強く結合し、次に加算演算子、比較演算子、&&(論理AND)、そして最後に||(論理OR)が続きます。

Precedence    Operator
    5             *  /  %  <<  >>  &  &^
    4             +  -  |  ^
    3             ==  !=  <  <=  >  >=
    2             &&
    1             ||

同じ優先順位の二項演算子は左から右に結合します。たとえば、x / y * z(x / y) * zと同じです。

+x                         // x
42 + a - b                 // (42 + a) - b
23 + 3*x[i]                // 23 + (3 * x[i])
x <= f()                   // x <= f()
^a >> b                    // (^a) >> b
f() || g()                 // f() || g()
x == y+1 && <-chanInt > 0  // (x == (y+1)) && ((<-chanInt) > 0)

算術演算子

算術演算子は数値に適用され、最初のオペランドと同じ型の結果を生成します。4つの標準算術演算子(+-*/)は整数浮動小数点複素数型に適用されます。+文字列にも適用されます。ビット論理演算子とシフト演算子は整数にのみ適用されます。

+    sum                    integers, floats, complex values, strings
-    difference             integers, floats, complex values
*    product                integers, floats, complex values
/    quotient               integers, floats, complex values
%    remainder              integers

&    bitwise AND            integers
|    bitwise OR             integers
^    bitwise XOR            integers
&^   bit clear (AND NOT)    integers

<<   left shift             integer << integer >= 0
>>   right shift            integer >> integer >= 0

オペランド型が型パラメータである場合、演算子はその型セット内の各型に適用されなければなりません。オペランドは、型パラメータがインスタンス化される型引数の値として表現され、演算はその型引数の精度で計算されます。たとえば、関数が与えられた場合

func dotProduct[F ~float32|~float64](v1, v2 []F) F {
	var s F
	for i, x := range v1 {
		y := v2[i]
		s += x * y
	}
	return s
}

x * yと加算s += x * yは、Fの型引数に応じて、それぞれfloat32またはfloat64の精度で計算されます。

整数演算子

2つの整数値xyの場合、整数商q = x / yと剰余r = x % yは次の関係を満たします。

x = q*y + r  and  |r| < |y|

x / yはゼロ方向に切り捨てられます(「切り捨て除算」)。

 x     y     x / y     x % y
 5     3       1         2
-5     3      -1        -2
 5    -3      -1         2
-5    -3       1        -2

このルールの唯一の例外は、被除数xxのint型の最も負の値である場合、2の補数整数オーバーフローにより、商q = x / -1xに等しくなります(そしてr = 0)。

                         x, q
int8                     -128
int16                  -32768
int32             -2147483648
int64    -9223372036854775808

除数が定数の場合、ゼロであってはなりません。ランタイムに除数がゼロの場合、ランタイムパニックが発生します。被除数が非負であり、除数が2の定数乗である場合、除算は右シフトに置き換えられ、剰余の計算はビットAND操作に置き換えられる場合があります。

 x     x / 4     x % 4     x >> 2     x & 3
 11      2         3         2          3
-11     -2        -3        -3          1

シフト演算子は、右オペランドで指定されたシフトカウント(非負でなければならない)だけ左オペランドをシフトします。ランタイムにシフトカウントが負の場合、ランタイムパニックが発生します。シフト演算子は、左オペランドが符号付き整数である場合は算術シフトを、符号なし整数である場合は論理シフトを実装します。シフトカウントに上限はありません。シフトは、左オペランドがシフトカウントnに対してn回1ずつシフトされたかのように動作します。結果として、x << 1x*2と同じであり、x >> 1x/2と同じですが、負の無限大方向に切り捨てられます。

整数オペランドの場合、単項演算子+-、および^は次のように定義されます。

+x                          is 0 + x
-x    negation              is 0 - x
^x    bitwise complement    is m ^ x  with m = "all bits set to 1" for unsigned x
                                      and  m = -1 for signed x

整数オーバーフロー

符号なし整数値の場合、演算+-*、および<<は2nの剰余として計算されます。ここでnは符号なし整数の型のビット幅です。大まかに言えば、これらの符号なし整数演算はオーバーフロー時に上位ビットを破棄し、プログラムは「ラップアラウンド」に依存することができます。

符号付き整数の場合、演算+-*/、および<<は合法的にオーバーフローする可能性があり、結果の値は存在し、符号付き整数表現、演算、およびそのオペランドによって確定的に定義されます。オーバーフローはランタイムパニックを引き起こしません。コンパイラは、オーバーフローが発生しないという仮定でコードを最適化することはできません。たとえば、x < x + 1が常に真であると仮定することはできません。

浮動小数点演算子

浮動小数点数と複素数の場合、+xxと同じであり、-xxの否定です。ゼロによる浮動小数点数または複素数の除算の結果は、IEEE 754標準以外では指定されていません。ランタイムパニックが発生するかどうかは、実装固有です。

実装は、複数の浮動小数点演算を単一の結合演算に結合する可能性があり、ステートメントをまたがることもあり、個々の命令を実行して丸めることで得られる値とは異なる結果を生成する可能性があります。明示的な浮動小数点型変換は、ターゲット型の精度に丸められ、その丸めを破棄するような結合を防ぎます。

たとえば、一部のアーキテクチャでは、「融合乗算加算」(FMA)命令が提供されており、中間結果x*yを丸めることなくx*y + zを計算します。これらの例は、Goの実装がその命令をいつ使用できるかを示しています。

// FMA allowed for computing r, because x*y is not explicitly rounded:
r  = x*y + z
r  = z;   r += x*y
t  = x*y; r = t + z
*p = x*y; r = *p + z
r  = x*y + float64(z)

// FMA disallowed for computing r, because it would omit rounding of x*y:
r  = float64(x*y) + z
r  = z; r += float64(x*y)
t  = float64(x*y); r = t + z

文字列連結

文字列は、+演算子または+=代入演算子を使用して連結できます。

s := "hi" + string(c)
s += " and good bye"

文字列の加算は、オペランドを連結して新しい文字列を作成します。

比較演算子

比較演算子は2つのオペランドを比較し、型なしのブール値を生成します。

==    equal
!=    not equal
<     less
<=    less or equal
>     greater
>=    greater or equal

すべての比較において、最初のオペランドは2番目のオペランドの型に代入可能であるか、またはその逆である必要があります。

等価演算子==!=は、比較可能な型のオペランドに適用されます。順序演算子<<=>、および>=は、順序付け可能な型のオペランドに適用されます。これらの用語と比較の結果は次のように定義されます。

  • ブール型は比較可能です。2つのブール値は、両方ともtrueであるか、両方ともfalseである場合に等しくなります。
  • 整数型は比較可能で順序付け可能です。2つの整数値は通常の方法で比較されます。
  • 浮動小数点型は比較可能で順序付け可能です。2つの浮動小数点値は、IEEE 754標準で定義されているように比較されます。
  • 複素数型は比較可能です。2つの複素数値uvは、real(u) == real(v)imag(u) == imag(v)の両方が成り立つ場合に等しくなります。
  • 文字列型は比較可能で順序付け可能です。2つの文字列値は辞書的にバイト単位で比較されます。
  • ポインタ型は比較可能です。2つのポインタ値は、同じ変数を指しているか、両方ともnil値を持つ場合に等しくなります。異なるゼロサイズ変数へのポインタは、等しい場合も等しくない場合もあります。
  • チャネル型は比較可能です。2つのチャネル値は、makeへの同じ呼び出しによって作成されたか、両方ともnil値を持つ場合に等しくなります。
  • 型パラメータではないインターフェース型は比較可能です。2つのインターフェース値は、同一の動的型と等しい動的値を持つ場合、または両方ともnil値を持つ場合に等しくなります。
  • 非インターフェース型Xの値xとインターフェース型Tの値tは、型Xが比較可能でXT実装している場合に比較できます。tの動的型がXと同一で、tの動的値がxと等しい場合に、それらは等しくなります。
  • 構造体型は、すべてのフィールド型が比較可能な場合に比較可能です。2つの構造体値は、対応する非ブランクフィールド値が等しい場合に等しくなります。フィールドはソース順に比較され、2つのフィールド値が異なる(またはすべてのフィールドが比較された)時点で比較は停止します。
  • 配列型は、配列要素型が比較可能な場合に比較可能です。2つの配列値は、対応する要素値が等しい場合に等しくなります。要素は昇順インデックスで比較され、2つの要素値が異なる(またはすべての要素が比較された)時点で比較は停止します。
  • 型パラメータは、厳密に比較可能であれば(下記参照)比較可能です。

同一の動的型を持つ2つのインターフェース値の比較は、その型が比較可能でない場合にランタイムパニックを引き起こします。この動作は、直接的なインターフェース値の比較だけでなく、インターフェース値を持つ配列や、インターフェース値を持つフィールドを持つ構造体を比較する場合にも適用されます。

スライス、マップ、および関数型は比較できません。ただし、特殊なケースとして、スライス、マップ、または関数値は、事前に宣言された識別子nilと比較できます。ポインタ、チャネル、およびインターフェース値をnilと比較することも許可されており、上記の一般ルールに従います。

const c = 3 < 4            // c is the untyped boolean constant true

type MyBool bool
var x, y int
var (
	// The result of a comparison is an untyped boolean.
	// The usual assignment rules apply.
	b3        = x == y // b3 has type bool
	b4 bool   = x == y // b4 has type bool
	b5 MyBool = x == y // b5 has type MyBool
)

型は、比較可能であり、インターフェース型でもインターフェース型で構成されてもいない場合に厳密に比較可能です。具体的には、

  • ブール型、数値型、文字列型、ポインタ型、チャネル型は厳密に比較可能です。
  • 構造体型は、そのすべてのフィールド型が厳密に比較可能な場合に厳密に比較可能です。
  • 配列型は、その配列要素型が厳密に比較可能な場合に厳密に比較可能です。
  • 型パラメータは、その型セット内のすべての型が厳密に比較可能な場合に厳密に比較可能です。

論理演算子

論理演算子はブール値に適用され、オペランドと同じ型の結果を生成します。左オペランドが評価され、条件が必要な場合にのみ右オペランドが評価されます。

&&    conditional AND    p && q  is  "if p then q else false"
||    conditional OR     p || q  is  "if p then true else q"
!     NOT                !p      is  "not p"

アドレス演算子

Tのオペランドxの場合、アドレス操作&xxへの型*Tのポインタを生成します。オペランドはアドレス可能である必要があり、つまり、変数、ポインタ間接参照、またはスライスインデックス操作、あるいはアドレス可能な構造体オペランドのフィールドセレクタ、またはアドレス可能な配列の配列インデックス操作である必要があります。アドレス可能性要件の例外として、xは(おそらく括弧で囲まれた)複合リテラルである場合もあります。xの評価がランタイムパニックを引き起こす場合、&xの評価も同様に引き起こします。

ポインタ型*Tのオペランドxの場合、ポインタ間接参照*xxが指す型T変数を表します。xnilの場合、*xを評価しようとするとランタイムパニックが発生します。

&x
&a[f(2)]
&Point{2, 3}
*p
*pf(x)

var x *int = nil
*x   // causes a run-time panic
&*x  // causes a run-time panic

受信演算子

チャネル型のオペランドchの場合、受信操作<-chの値はチャネルchから受信した値です。チャネルの方向は受信操作を許可する必要があり、受信操作の型はチャネルの要素型です。式は値が利用可能になるまでブロックします。nilチャネルからの受信は永久にブロックします。閉じられたチャネルに対する受信操作は常にすぐに進行でき、以前に送信された値がすべて受信された後、要素型のゼロ値を返します。

v1 := <-ch
v2 = <-ch
f(<-ch)
<-strobe  // wait until clock pulse and discard received value

オペランド型が型パラメータである場合、その型セット内のすべての型は受信操作を許可するチャネル型でなければならず、すべて同じ要素型を持たなければなりません。これが受信操作の型となります。

特殊な形式の代入ステートメントまたは初期化で使用される受信式

x, ok = <-ch
x, ok := <-ch
var x, ok = <-ch
var x, ok T = <-ch

通信が成功したかどうかを報告する追加の型なしブール結果を生成します。okの値は、受信した値がチャネルへの送信操作の成功によって配信された場合はtrue、チャネルが閉じられて空であるために生成されたゼロ値の場合はfalseです。

変換

変換は、式のを、変換によって指定された型に変更します。変換はソースに文字通り現れることもあれば、式が現れるコンテキストによって暗黙的に行われることもあります。

明示的な変換は、T(x)という形式の式です。ここで、Tは型であり、xは型Tに変換できる式です。

Conversion = Type "(" Expression [ "," ] ")" .

型が演算子*または<-で始まる場合、または型がキーワードfuncで始まり結果リストがない場合、曖昧さを避けるために必要に応じて括弧で囲む必要があります。

*Point(p)        // same as *(Point(p))
(*Point)(p)      // p is converted to *Point
<-chan int(c)    // same as <-(chan int(c))
(<-chan int)(c)  // c is converted to <-chan int
func()(x)        // function signature func() x
(func())(x)      // x is converted to func()
(func() int)(x)  // x is converted to func() int
func() int(x)    // x is converted to func() int (unambiguous)

定数xは、xTの値によって表現可能である場合、型Tに変換できます。特殊なケースとして、整数定数xは、非定数xの場合と同じルールを使用して、文字列型に明示的に変換できます。

定数を型パラメータではない型に変換すると、型付けされた定数が生成されます。

uint(iota)               // iota value of type uint
float32(2.718281828)     // 2.718281828 of type float32
complex128(1)            // 1.0 + 0.0i of type complex128
float32(0.49999999)      // 0.5 of type float32
float64(-1e-1000)        // 0.0 of type float64
string('x')              // "x" of type string
string(0x266c)           // "♬" of type string
myString("foo" + "bar")  // "foobar" of type myString
string([]byte{'a'})      // not a constant: []byte{'a'} is not a constant
(*int)(nil)              // not a constant: nil is not a constant, *int is not a boolean, numeric, or string type
int(1.2)                 // illegal: 1.2 cannot be represented as an int
string(65.0)             // illegal: 65.0 is not an integer constant

定数を型パラメータに変換すると、その型の非定数値が生成されます。その値は、型パラメータがインスタンス化される型引数の値として表現されます。たとえば、関数が与えられた場合

func f[P ~float32|~float64]() {
	… P(1.1) …
}

変換P(1.1)は、型Pの非定数値をもたらし、値1.1は、fの型引数に応じてfloat32またはfloat64として表現されます。したがって、ffloat32型でインスタンス化された場合、式P(1.1) + 1.2の数値は、対応する非定数float32加算と同じ精度で計算されます。

非定数値xは、次のいずれかのケースで型Tに変換できます。

  • xT代入可能です。
  • 構造体タグを無視すると(下記参照)、xの型とT型パラメータではないが、同一基底型を持ちます。
  • 構造体タグを無視すると(下記参照)、xの型とTは両方とも名前付き型ではないポインタ型であり、そのポインタの基本型は型パラメータではないが、同一の基底型を持ちます。
  • xの型とTは両方とも整数型または浮動小数点型です。
  • xの型とTは両方とも複素数型です。
  • xは整数またはバイトまたはルーンのスライスであり、Tは文字列型です。
  • xは文字列であり、Tはバイトまたはルーンのスライスです。
  • xはスライス、Tは配列[Go 1.20]または配列へのポインタ[Go 1.17]であり、スライス型と配列型は同一の要素型を持ちます。

さらに、Tまたはxの型Vが型パラメータである場合、次のいずれかの条件が適用されれば、xを型Tに変換することもできます。

  • VTの両方が型パラメータであり、Vの型セット内の各型の値をTの型セット内の各型に変換できる場合。
  • Vのみが型パラメータであり、Vの型セット内の各型の値をTに変換できる場合。
  • Tのみが型パラメータであり、xTの型セット内の各型に変換できる場合。

変換の目的で構造体型を比較する場合、構造体タグは無視されます。

type Person struct {
	Name    string
	Address *struct {
		Street string
		City   string
	}
}

var data *struct {
	Name    string `json:"name"`
	Address *struct {
		Street string `json:"street"`
		City   string `json:"city"`
	} `json:"address"`
}

var person = (*Person)(data)  // ignoring tags, the underlying types are identical

数値型間の(非定数)変換、または文字列型への/からの変換には、特定のルールが適用されます。これらの変換はxの表現を変更し、ランタイムコストが発生する可能性があります。他のすべての変換は、型のみを変更し、xの表現は変更しません。

ポインタと整数の間で変換する言語メカニズムはありません。unsafeパッケージは、制限された状況下でこの機能を実現します。

数値型間の変換

非定数数値の変換には、次の規則が適用されます。

  1. 整数型間で変換する場合、値が符号付き整数であれば、暗黙的な無限精度に符号拡張されます。そうでなければ、ゼロ拡張されます。その後、結果型のサイズに収まるように切り捨てられます。たとえば、v := uint16(0x10F0)の場合、uint32(int8(v)) == 0xFFFFFFF0となります。変換は常に有効な値を生成し、オーバーフローの兆候はありません。
  2. 浮動小数点数を整数に変換する場合、小数部分は破棄されます(ゼロ方向への切り捨て)。
  3. 整数または浮動小数点数を浮動小数点型に変換する場合、または複素数を別の複素数型に変換する場合、結果の値は宛先型によって指定された精度に丸められます。たとえば、float32型の変数xの値は、IEEE 754の32ビット数よりも追加の精度を使用して格納される場合がありますが、float32(x)xの値を32ビット精度に丸めた結果を表します。同様に、x + 0.1は32ビットよりも多くの精度を使用する場合がありますが、float32(x + 0.1)はそうではありません。

浮動小数点数または複素数を含むすべての非定数変換において、結果型が値を表現できない場合でも変換は成功しますが、結果の値は実装依存です。

文字列型への/からの変換

  1. バイトスライスを文字列型に変換すると、連続するバイトがスライスの要素である文字列が生成されます。
    string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'})   // "hellø"
    string([]byte{})                                     // ""
    string([]byte(nil))                                  // ""
    
    type bytes []byte
    string(bytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'})    // "hellø"
    
    type myByte byte
    string([]myByte{'w', 'o', 'r', 'l', 'd', '!'})       // "world!"
    myString([]myByte{'\xf0', '\x9f', '\x8c', '\x8d'})   // "🌍"
    
  2. ルーンスライスを文字列型に変換すると、個々のルーン値を文字列に変換して連結した文字列が生成されます。
    string([]rune{0x767d, 0x9d6c, 0x7fd4})   // "\u767d\u9d6c\u7fd4" == "白鵬翔"
    string([]rune{})                         // ""
    string([]rune(nil))                      // ""
    
    type runes []rune
    string(runes{0x767d, 0x9d6c, 0x7fd4})    // "\u767d\u9d6c\u7fd4" == "白鵬翔"
    
    type myRune rune
    string([]myRune{0x266b, 0x266c})         // "\u266b\u266c" == "♫♬"
    myString([]myRune{0x1f30e})              // "\U0001f30e" == "🌎"
    
  3. 文字列型の値をバイトスライス型に変換すると、連続する要素が文字列のバイトである、nilではないスライスが生成されます。結果のスライスの容量は実装固有であり、スライスの長さよりも大きい場合があります。
    []byte("hellø")             // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    []byte("")                  // []byte{}
    
    bytes("hellø")              // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    
    []myByte("world!")          // []myByte{'w', 'o', 'r', 'l', 'd', '!'}
    []myByte(myString("🌏"))    // []myByte{'\xf0', '\x9f', '\x8c', '\x8f'}
    
  4. 文字列型の値をルーンスライス型に変換すると、文字列の個々のUnicodeコードポイントを含むスライスが生成されます。結果のスライスの容量は実装固有であり、スライスの長さよりも大きい場合があります。
    []rune(myString("白鵬翔"))   // []rune{0x767d, 0x9d6c, 0x7fd4}
    []rune("")                  // []rune{}
    
    runes("白鵬翔")              // []rune{0x767d, 0x9d6c, 0x7fd4}
    
    []myRune("♫♬")              // []myRune{0x266b, 0x266c}
    []myRune(myString("🌐"))    // []myRune{0x1f310}
    
  5. 最後に、歴史的な理由から、整数値を文字列型に変換できます。この形式の変換は、与えられた整数値を持つUnicodeコードポイントの(多バイトになる可能性のある)UTF-8表現を含む文字列を生成します。有効なUnicodeコードポイントの範囲外の値は、"\uFFFD"に変換されます。
    string('a')          // "a"
    string(65)           // "A"
    string('\xf8')       // "\u00f8" == "ø" == "\xc3\xb8"
    string(-1)           // "\ufffd" == "\xef\xbf\xbd"
    
    type myString string
    myString('\u65e5')   // "\u65e5" == "日" == "\xe6\x97\xa5"
    
    注:この形式の変換は、最終的に言語から削除される可能性があります。go vetツールは、特定の整数から文字列への変換を潜在的なエラーとしてフラグ付けします。代わりに、utf8.AppendRuneutf8.EncodeRuneなどのライブラリ関数を使用する必要があります。

スライスから配列または配列ポインタへの変換

スライスを配列に変換すると、スライスの基になる配列の要素を含む配列が生成されます。同様に、スライスを配列ポインタに変換すると、スライスの基になる配列へのポインタが生成されます。どちらの場合も、スライスの長さが配列の長さより短い場合、ランタイムパニックが発生します。

s := make([]byte, 2, 4)

a0 := [0]byte(s)
a1 := [1]byte(s[1:])     // a1[0] == s[1]
a2 := [2]byte(s)         // a2[0] == s[0]
a4 := [4]byte(s)         // panics: len([4]byte) > len(s)

s0 := (*[0]byte)(s)      // s0 != nil
s1 := (*[1]byte)(s[1:])  // &s1[0] == &s[1]
s2 := (*[2]byte)(s)      // &s2[0] == &s[0]
s4 := (*[4]byte)(s)      // panics: len([4]byte) > len(s)

var t []string
t0 := [0]string(t)       // ok for nil slice t
t1 := (*[0]string)(t)    // t1 == nil
t2 := (*[1]string)(t)    // panics: len([1]string) > len(t)

u := make([]byte, 0)
u0 := (*[0]byte)(u)      // u0 != nil

定数式

定数式は定数オペランドのみを含み、コンパイル時に評価されます。

型なしのブール型、数値型、文字列型の定数は、それぞれブール型、数値型、文字列型のオペランドを使用することが合法である場所であればどこでも使用できます。

定数比較は常に型なしのブール定数を生成します。定数シフト式の左オペランドが型なし定数である場合、結果は整数定数です。それ以外の場合、それは左オペランドと同じ型の定数であり、整数型でなければなりません。

型なし定数に対する他の操作は、同じ種類の型なし定数、つまりブール、整数、浮動小数点、複素数、または文字列定数になります。二項演算 (シフト以外) の型なしオペランドの種類が異なる場合、結果は、このリストで後に出てくるオペランドの種類になります: 整数、ルーン、浮動小数点、複素数。例えば、型なし整数定数を型なし複素数定数で割ると、型なし複素数定数が得られます。

const a = 2 + 3.0          // a == 5.0   (untyped floating-point constant)
const b = 15 / 4           // b == 3     (untyped integer constant)
const c = 15 / 4.0         // c == 3.75  (untyped floating-point constant)
const Θ float64 = 3/2      // Θ == 1.0   (type float64, 3/2 is integer division)
const Π float64 = 3/2.     // Π == 1.5   (type float64, 3/2. is float division)
const d = 1 << 3.0         // d == 8     (untyped integer constant)
const e = 1.0 << 3         // e == 8     (untyped integer constant)
const f = int32(1) << 33   // illegal    (constant 8589934592 overflows int32)
const g = float64(2) >> 1  // illegal    (float64(2) is a typed floating-point constant)
const h = "foo" > "bar"    // h == true  (untyped boolean constant)
const j = true             // j == true  (untyped boolean constant)
const k = 'w' + 1          // k == 'x'   (untyped rune constant)
const l = "hi"             // l == "hi"  (untyped string constant)
const m = string(k)        // m == "x"   (type string)
const Σ = 1 - 0.707i       //            (untyped complex constant)
const Δ = Σ + 2.0e-4       //            (untyped complex constant)
const Φ = iota*1i - 1/1i   //            (untyped complex constant)

組込み関数complexを型なし整数、ルーン、または浮動小数点定数に適用すると、型なし複素数定数が得られます。

const ic = complex(0, c)   // ic == 3.75i  (untyped complex constant)
const iΘ = complex(0, Θ)   // iΘ == 1i     (type complex128)

定数式は常に厳密に評価されます。中間値と定数自体は、言語で定義済みの型がサポートするよりもかなり大きな精度を必要とする場合があります。以下は有効な宣言です。

const Huge = 1 << 100         // Huge == 1267650600228229401496703205376  (untyped integer constant)
const Four int8 = Huge >> 98  // Four == 4                                (type int8)

定数除算または剰余演算の除数はゼロであってはなりません。

3.14 / 0.0   // illegal: division by zero

型付き定数の値は常に、定数型の値によって正確に表現可能でなければなりません。以下の定数式は不正です。

uint(-1)     // -1 cannot be represented as a uint
int(3.14)    // 3.14 cannot be represented as an int
int64(Huge)  // 1267650600228229401496703205376 cannot be represented as an int64
Four * 300   // operand 300 cannot be represented as an int8 (type of Four)
Four * 100   // product 400 cannot be represented as an int8 (type of Four)

単項ビット補数演算子^によって使用されるマスクは、非定数の規則に一致します。マスクは符号なし定数ではすべて1、符号付きおよび型なし定数では-1です。

^1         // untyped integer constant, equal to -2
uint8(^1)  // illegal: same as uint8(-2), -2 cannot be represented as a uint8
^uint8(1)  // typed uint8 constant, same as 0xFF ^ uint8(1) = uint8(0xFE)
int8(^1)   // same as int8(-2)
^int8(1)   // same as -1 ^ int8(1) = -2

実装上の制限: コンパイラは、型なし浮動小数点または複素数定数式を計算する際に丸めを使用する場合があります。詳しくは定数のセクションにある実装上の制限を参照してください。この丸めにより、無限精度で計算した場合に整数となる浮動小数点定数式が、整数コンテキストで無効になることや、その逆が発生する可能性があります。

評価順序

パッケージレベルでは、初期化の依存関係変数宣言における個々の初期化式の評価順序を決定します。それ以外の場合、式、代入、またはreturn ステートメントオペランドを評価する際には、すべての関数呼び出し、メソッド呼び出し、受信操作、および二項論理演算は、字句的な左から右への順序で評価されます。

例えば、(関数ローカルの) 代入

y[f()], ok = g(z || h(), i()+x[j()], <-c), k()

における関数呼び出しと通信は、f(), h() (z が false と評価された場合), i(), j(), <-c, g(), k() の順で発生します。ただし、これらのイベントの順序と、x の評価およびインデックス付け、ならびに yz の評価との比較は、字句的に要求される場合を除き、指定されていません。例えば、g はその引数が評価される前に呼び出すことはできません。

a := 1
f := func() int { a++; return a }
x := []int{a, f()}            // x may be [1, 2] or [2, 2]: evaluation order between a and f() is not specified
m := map[int]int{a: 1, a: 2}  // m may be {2: 1} or {2: 2}: evaluation order between the two map assignments is not specified
n := map[int]int{a: f()}      // n may be {2: 3} or {3: 3}: evaluation order between the key and the value is not specified

パッケージレベルでは、初期化の依存関係が個々の初期化式の左から右への規則を上書きしますが、各式内のオペランドに対しては上書きしません。

var a, b, c = f() + v(), g(), sqr(u()) + v()

func f() int        { return c }
func g() int        { return a }
func sqr(x int) int { return x*x }

// functions u and v are independent of all other variables and functions

関数呼び出しは、u()sqr()v()f()v()g() の順で行われます。

単一式内の浮動小数点演算は、演算子の結合性に従って評価されます。明示的な括弧は、デフォルトの結合性を上書きすることで評価に影響を与えます。式 x + (y + z) では、x を加算する前に加算 y + z が実行されます。

ステートメント

ステートメントは実行を制御します。

Statement  = Declaration | LabeledStmt | SimpleStmt |
             GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
             FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
             DeferStmt .

SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .

終了ステートメント

終了ステートメントは、ブロックにおける通常の制御フローを中断します。以下のステートメントは終了ステートメントです。

  1. "return"または"goto"ステートメント。
  2. 組み込み関数panicの呼び出し。
  3. ブロックにおいて、ステートメントリストが終了ステートメントで終わる場合。
  4. "if"ステートメントにおいて
    • "else"ブランチが存在し、かつ
    • 両方のブランチが終了ステートメントである場合。
  5. "for" ステートメントにおいて
    • "for" ステートメントを参照する "break" ステートメントがなく、かつ
    • ループ条件が指定されておらず、かつ
    • "for" ステートメントが range 句を使用しない場合。
  6. "switch" ステートメントにおいて
    • "switch" ステートメントを参照する "break" ステートメントがなく、
    • default ケースがあり、かつ
    • 各ケースのステートメントリスト(default を含む)が、終了ステートメント、または場合によってはラベル付きの"fallthrough" ステートメントで終わる場合。
  7. "select" ステートメントにおいて
    • "select" ステートメントを参照する "break" ステートメントがなく、かつ
    • 各ケースのステートメントリスト(default があればそれも含む)が終了ステートメントで終わる場合。
  8. 終了ステートメントにラベル付けされたラベル付きステートメント

他のすべてのステートメントは終了ステートメントではありません。

ステートメントリストは、そのリストが空ではなく、かつ最後の空ではないステートメントが終了ステートメントである場合に、終了ステートメントで終わります。

空のステートメント

空のステートメントは何もしません。

EmptyStmt = .

ラベル付きステートメント

ラベル付きステートメントは、gotobreak、または continue ステートメントのターゲットとなることができます。

LabeledStmt = Label ":" Statement .
Label       = identifier .
Error: log.Panic("error encountered")

式ステートメント

特定の組み込み関数を除き、関数とメソッドの呼び出しおよび受信操作は、ステートメントコンテキストに現れることがあります。そのようなステートメントは括弧で囲むことができます。

ExpressionStmt = Expression .

以下の組み込み関数はステートメントコンテキストでは許可されません。

append cap complex imag len make new real
unsafe.Add unsafe.Alignof unsafe.Offsetof unsafe.Sizeof unsafe.Slice unsafe.SliceData unsafe.String unsafe.StringData
h(x+y)
f.Close()
<-ch
(<-ch)
len("foo")  // illegal if len is the built-in function

送信ステートメント

送信ステートメントは、チャネルに値を送信します。チャネル式はチャネル型でなければならず、チャネル方向は送信操作を許可し、送信される値の型はチャネルの要素型に代入可能でなければなりません。

SendStmt = Channel "<-" Expression .
Channel  = Expression .

通信が開始される前に、チャネルと値の両方の式が評価されます。送信が進行するまで通信はブロックされます。バッファなしチャネルへの送信は、受信者が準備できていれば進行できます。バッファ付きチャネルへの送信は、バッファに空きがあれば進行できます。クローズされたチャネルへの送信は、実行時パニックを引き起こして進行します。nilチャネルへの送信は永遠にブロックされます。

ch <- 3  // send value 3 to channel ch

チャネル式の型が型パラメータである場合、その型集合内のすべての型は送信操作を許可するチャネル型でなければならず、それらはすべて同じ要素型を持たなければならず、送信される値の型はその要素型に代入可能でなければなりません。

インクリメント/デクリメントステートメント

"++"と"--"ステートメントは、オペランドを型なし定数1でインクリメントまたはデクリメントします。代入と同様に、オペランドはアドレス指定可能であるか、マップインデックス式でなければなりません。

IncDecStmt = Expression ( "++" | "--" ) .

以下の代入ステートメントは意味的に同等です。

IncDec statement    Assignment
x++                 x += 1
x--                 x -= 1

代入ステートメント

代入は、変数に格納されている現在の値を、で指定された新しい値に置き換えます。代入ステートメントは、単一の値を単一の変数に代入したり、複数の値を一致する数の変数に代入したりできます。

Assignment = ExpressionList assign_op ExpressionList .

assign_op  = [ add_op | mul_op ] "=" .

各左辺オペランドは、アドレス指定可能であるか、マップインデックス式であるか、または(=代入の場合のみ)ブランク識別子である必要があります。オペランドは括弧で囲むことができます。

x = 1
*p = f()
a[i] = 23
(k) = <-ch  // same as: k = <-ch

代入演算 x op= y において、op が二項算術演算子である場合、それは x = x op (y) と等価ですが、x は一度だけ評価されます。op= 構造体は単一のトークンです。代入演算では、左辺と右辺の式リストは両方とも厳密に1つの単一値式を含んでいなければならず、左辺式はブランク識別子であってはなりません。

a[i] <<= 2
i &^= 1<<n

タプル代入は、多値演算の個々の要素を変数リストに代入します。これには2つの形式があります。1つ目の形式では、右辺オペランドは関数呼び出し、チャネルまたはマップ演算、あるいは型アサーションのような単一の多値式です。左辺のオペランドの数は値の数と一致しなければなりません。例えば、fが2つの値を返す関数である場合、

x, y = f()

は最初の値をxに、2番目の値をyに代入します。2番目の形式では、左辺のオペランドの数は右辺の式の数と等しくなければならず、各式は単一値でなければならず、右辺のn番目の式が左辺のn番目のオペランドに代入されます。

one, two, three = '一', '二', '三'

ブランク識別子は、代入における右辺の値を無視する方法を提供します。

_ = x       // evaluate x but ignore it
x, _ = f()  // evaluate f() but ignore second result value

代入は2つのフェーズで進行します。まず、左辺のインデックス式ポインタ間接参照セレクタにおける暗黙的なポインタ間接参照を含む)のオペランドと、右辺の式はすべて通常の順序で評価されます。次に、代入は左から右の順序で実行されます。

a, b = b, a  // exchange a and b

x := []int{1, 2, 3}
i := 0
i, x[i] = 1, 2  // set i = 1, x[0] = 2

i = 0
x[i], i = 2, 1  // set x[0] = 2, i = 1

x[0], x[0] = 1, 2  // set x[0] = 1, then x[0] = 2 (so x[0] == 2 at end)

x[1], x[3] = 4, 5  // set x[1] = 4, then panic setting x[3] = 5.

type Point struct { x, y int }
var p *Point
x[2], p.x = 6, 7  // set x[2] = 6, then panic setting p.x = 7

i = 2
x = []int{3, 5, 7}
for i, x[i] = range x {  // set i, x[2] = 0, x[0]
	break
}
// after this loop, i == 0 and x is []int{3, 5, 3}

代入では、各値は、以下の特別なケースを除き、代入されるオペランドの型に代入可能でなければなりません。

  1. 任意の型付き値は、ブランク識別子に代入できます。
  2. 型なし定数がインターフェース型またはブランク識別子の変数に代入される場合、その定数はまず暗黙的にそのデフォルト型変換されます。
  3. 型なしブール値がインターフェース型またはブランク識別子の変数に代入される場合、まず暗黙的に型 bool に変換されます。

値が変数に代入されると、変数に格納されているデータのみが置き換えられます。値に参照が含まれている場合、代入はその参照をコピーしますが、参照されているデータ(スライスの基底配列など)のコピーは作成しません。

var s1 = []int{1, 2, 3}
var s2 = s1                    // s2 stores the slice descriptor of s1
s1 = s1[:1]                    // s1's length is 1 but it still shares its underlying array with s2
s2[0] = 42                     // setting s2[0] changes s1[0] as well
fmt.Println(s1, s2)            // prints [42] [42 2 3]

var m1 = make(map[string]int)
var m2 = m1                    // m2 stores the map descriptor of m1
m1["foo"] = 42                 // setting m1["foo"] changes m2["foo"] as well
fmt.Println(m2["foo"])         // prints 42

If ステートメント

"If" ステートメントは、ブール式の値に基づいて2つのブランチの条件付き実行を指定します。式がtrueと評価された場合、"if" ブランチが実行され、そうでない場合、存在すれば"else" ブランチが実行されます。

IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .
if x > max {
	x = max
}

式の前に単純なステートメントを置くことができ、そのステートメントは式が評価される前に実行されます。

if x := f(); x < y {
	return x
} else if x > z {
	return z
} else {
	return y
}

Switch ステートメント

"Switch" ステートメントは多分岐実行を提供します。式または型が "switch" 内の "case" と比較され、どの分岐を実行するかを決定します。

SwitchStmt = ExprSwitchStmt | TypeSwitchStmt .

には、式スイッチと型スイッチの2つの形式があります。式スイッチでは、ケースにスイッチ式の値と比較される式が含まれます。型スイッチでは、ケースに、特別に注釈付けされたスイッチ式の型と比較される型が含まれます。スイッチ式は、スイッチステートメント内で厳密に一度だけ評価されます。

式スイッチ

式スイッチでは、スイッチ式が評価され、定数である必要のないケース式は左から右、上から下の順に評価されます。スイッチ式と等しい最初のものが関連するケースのステートメントの実行をトリガーし、他のケースはスキップされます。一致するケースがなく、「default」ケースがある場合、そのステートメントが実行されます。デフォルトケースは最大1つまでしか存在せず、「switch」ステートメントのどこにでも配置できます。スイッチ式が省略された場合、ブール値trueと同等です。

ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
ExprCaseClause = ExprSwitchCase ":" StatementList .
ExprSwitchCase = "case" ExpressionList | "default" .

スイッチ式が型なし定数と評価される場合、まず暗黙的にそのデフォルト型変換されます。定義済みで型なしの値nilは、スイッチ式として使用できません。スイッチ式の型は比較可能でなければなりません。

ケース式が型なしである場合、まず暗黙的にスイッチ式の型に変換されます。各(おそらく変換された)ケース式xとスイッチ式の値tについて、x == tは有効な比較でなければなりません。

言い換えれば、スイッチ式は、明示的な型なしで一時変数 t を宣言して初期化するために使用されたかのように扱われ、その t の値と各ケース式 x が等しいかどうかがテストされます。

ケース句またはデフォルト句において、最後の空でないステートメントは、この句の終わりから次の句の最初のステートメントに制御を移行させることを示す(場合によってはラベル付き"fallthrough" ステートメントであってもよい。そうでなければ、制御は"switch" ステートメントの終わりに移行する。"fallthrough" ステートメントは、式スイッチの最後の句以外のすべての句の最後のステートメントとして現れることができる。

スイッチ式の前に単純なステートメントを置くことができ、そのステートメントは式が評価される前に実行されます。

switch tag {
default: s3()
case 0, 1, 2, 3: s1()
case 4, 5, 6, 7: s2()
}

switch x := f(); {  // missing switch expression means "true"
case x < 0: return -x
default: return x
}

switch {
case x < y: f1()
case x < z: f2()
case x == 4: f3()
}

実装上の制限: コンパイラは、複数のケース式が同じ定数に評価されることを許可しない場合があります。例えば、現在のコンパイラは、ケース式で重複する整数、浮動小数点、または文字列定数を許可しません。

型スイッチ

型スイッチは値ではなく型を比較します。それ以外は式スイッチに似ています。キーワードtypeを使用して、実際の型ではなく型アサーションの形式を持つ特別なスイッチ式でマークされます。

switch x.(type) {
// cases
}

ケースは、実際の型Tを式xの動的な型と照合します。型アサーションと同様に、xインターフェース型でなければなりませんが、型パラメータであってはならず、ケースにリストされている非インターフェース型Tはそれぞれxの型を実装しなければなりません。型スイッチのケースにリストされている型はすべて異なっていなければなりません。

TypeSwitchStmt  = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" .
TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
TypeCaseClause  = TypeSwitchCase ":" StatementList .
TypeSwitchCase  = "case" TypeList | "default" .

TypeSwitchGuardには、短い変数宣言を含めることができます。その形式が使用される場合、変数は各句の暗黙のブロック内のTypeSwitchCaseの終わりに宣言されます。正確に1つの型をリストするケースのある句では、変数はその型を持ちます。それ以外の場合、変数はTypeSwitchGuard内の式の型を持ちます。

型に代わって、ケースはあらかじめ宣言された識別子nilを使用することができます。このケースは、TypeSwitchGuard内の式がnilインターフェース値である場合に選択されます。nilケースは最大1つまでしか存在できません。

interface{}の式xが与えられた場合、以下の型スイッチ

switch i := x.(type) {
case nil:
	printString("x is nil")                // type of i is type of x (interface{})
case int:
	printInt(i)                            // type of i is int
case float64:
	printFloat64(i)                        // type of i is float64
case func(int) float64:
	printFunction(i)                       // type of i is func(int) float64
case bool, string:
	printString("type is bool or string")  // type of i is type of x (interface{})
default:
	printString("don't know the type")     // type of i is type of x (interface{})
}

書き換えられる可能性があります

v := x  // x is evaluated exactly once
if v == nil {
	i := v                                 // type of i is type of x (interface{})
	printString("x is nil")
} else if i, isInt := v.(int); isInt {
	printInt(i)                            // type of i is int
} else if i, isFloat64 := v.(float64); isFloat64 {
	printFloat64(i)                        // type of i is float64
} else if i, isFunc := v.(func(int) float64); isFunc {
	printFunction(i)                       // type of i is func(int) float64
} else {
	_, isBool := v.(bool)
	_, isString := v.(string)
	if isBool || isString {
		i := v                         // type of i is type of x (interface{})
		printString("type is bool or string")
	} else {
		i := v                         // type of i is type of x (interface{})
		printString("don't know the type")
	}
}

型パラメータまたはジェネリック型は、ケース内の型として使用できます。インスタンス化時にその型がスイッチ内の別のエントリと重複することが判明した場合、最初に一致するケースが選択されます。

func f[P any](x any) int {
	switch x.(type) {
	case P:
		return 0
	case string:
		return 1
	case []P:
		return 2
	case []byte:
		return 3
	default:
		return 4
	}
}

var v1 = f[string]("foo")   // v1 == 0
var v2 = f[byte]([]byte{})  // v2 == 2

型スイッチガードの前に単純なステートメントを置くことができ、そのステートメントはガードが評価される前に実行されます。

「fallthrough」ステートメントは型スイッチでは許可されません。

For ステートメント

"for" ステートメントは、ブロックの繰り返し実行を指定します。これには3つの形式があります。繰り返しは、単一の条件、"for" 句、または "range" 句によって制御されます。

ForStmt   = "for" [ Condition | ForClause | RangeClause ] Block .
Condition = Expression .

単一条件の For ステートメント

最も単純な形式では、"for" ステートメントは、ブール条件がtrueと評価される限り、ブロックの繰り返し実行を指定します。条件は各イテレーションの前に評価されます。条件が指定されていない場合、それはブール値trueと同等です。

for a < b {
	a *= 2
}

for 句を持つ For ステートメント

ForClause を持つ "for" ステートメントもその条件によって制御されますが、さらに、代入、インクリメント、デクリメントステートメントなどのinitおよびpostステートメントを指定できます。init ステートメントは短い変数宣言であってもよいですが、post ステートメントであってはなりません。

ForClause = [ InitStmt ] ";" [ Condition ] ";" [ PostStmt ] .
InitStmt  = SimpleStmt .
PostStmt  = SimpleStmt .
for i := 0; i < 10; i++ {
	f(i)
}

init ステートメントが空でない場合、最初のイテレーションの条件を評価する前に一度実行されます。post ステートメントは、ブロックの各実行後(かつブロックが実行された場合のみ)に実行されます。ForClause のどの要素も空であってもよいですが、条件のみの場合を除き、セミコロンは必須です。条件が指定されていない場合、それはブール値trueと同等です。

for cond { S() }    is the same as    for ; cond ; { S() }
for      { S() }    is the same as    for true     { S() }

各反復には、独自の個別の宣言された変数(または複数変数)があります [Go 1.22]。最初の反復で使用される変数は、init ステートメントによって宣言されます。それに続く各反復で使用される変数は、post ステートメントを実行する前に暗黙的に宣言され、その時点での前の反復の変数の値に初期化されます。

var prints []func()
for i := 0; i < 5; i++ {
	prints = append(prints, func() { println(i) })
	i++
}
for _, p := range prints {
	p()
}

出力します

1
3
5

[Go 1.22] 以前は、各イテレーションは独自の個別の変数を持つのではなく、1つの変数セットを共有していました。その場合、上記の例は以下を出力します。

6
6
6

range 句を持つ For ステートメント

"range" 句を持つ "for" ステートメントは、配列、スライス、文字列、またはマップのすべてのエントリ、チャネルで受信された値、ゼロから上限までの整数値 [Go 1.22]、またはイテレータ関数の yield 関数に渡された値 [Go 1.23] を繰り返し処理します。各エントリについて、存在する場合、イテレーション値を対応するイテレーション変数に代入し、その後ブロックを実行します。

RangeClause = [ ExpressionList "=" | IdentifierList ":=" ] "range" Expression .

"range" 句の右側の式はレンジ式と呼ばれ、配列、配列へのポインタ、スライス、文字列、マップ、受信操作を許可するチャネル、整数、または特定のシグネチャを持つ関数(下記参照)のいずれかです。代入と同様に、左側のオペランドが存在する場合、それらはアドレス指定可能であるか、マップインデックス式でなければなりません。それらはイテレーション変数を表します。レンジ式が関数の場合、最大イテレーション変数の数は関数のシグネチャに依存します。レンジ式がチャネルまたは整数の場合、最大1つのイテレーション変数が許可されます。それ以外の場合は最大2つまで許可されます。最後のイテレーション変数がブランク識別子である場合、レンジ句はその識別子なしの同じ句と同等です。

レンジ式xはループを開始する前に評価されますが、1つの例外があります。つまり、最大1つのイテレーション変数が存在し、xまたはlen(x)定数である場合、レンジ式は評価されません。

左辺の関数呼び出しは、イテレーションごとに一度だけ評価されます。各イテレーションについて、対応するイテレーション変数が存在する場合、イテレーション値は次のように生成されます。

Range expression                                       1st value                2nd value

array or slice      a  [n]E, *[n]E, or []E             index    i  int          a[i]       E
string              s  string type                     index    i  int          see below  rune
map                 m  map[K]V                         key      k  K            m[k]       V
channel             c  chan E, <-chan E                element  e  E
integer value       n  integer type, or untyped int    value    i  see below
function, 0 values  f  func(func() bool)
function, 1 value   f  func(func(V) bool)              value    v  V
function, 2 values  f  func(func(K, V) bool)           key      k  K            v          V
  1. 配列、配列へのポインタ、またはスライス値aの場合、インデックスイテレーション値は要素インデックス0から始まる昇順で生成されます。イテレーション変数が最大1つだけの場合、レンジループは0からlen(a)-1までのイテレーション値を生成し、配列またはスライス自体へのインデックス付けは行いません。nilスライスの場合、イテレーション回数は0です。
  2. 文字列値の場合、"range" 句は、バイトインデックス0から始まる文字列内のUnicodeコードポイントを反復処理します。連続するイテレーションでは、インデックス値は文字列内の連続するUTF-8エンコードされたコードポイントの最初のバイトのインデックスとなり、2番目の値(型rune)は対応するコードポイントの値となります。イテレーションが無効なUTF-8シーケンスに遭遇した場合、2番目の値はUnicode置換文字である0xFFFDとなり、次のイテレーションは文字列内で1バイト進みます。
  3. マップの反復順序は指定されておらず、反復ごとに同じであることは保証されません。反復中にまだ到達していないマップエントリが削除された場合、対応する反復値は生成されません。反復中にマップエントリが作成された場合、そのエントリは反復中に生成される場合とスキップされる場合があります。選択は作成されたエントリごと、および反復ごとに異なる場合があります。マップがnilの場合、反復回数は0です。
  4. チャネルの場合、生成される反復値は、チャネルがクローズされるまでチャネルに送信された連続する値です。チャネルがnilの場合、レンジ式は永遠にブロックされます。
  5. 整数値 nn整数型または型なし整数定数)の場合、0 から n-1 までの反復値が昇順で生成されます。n が整数型の場合、反復値も同じ型を持ちます。それ以外の場合、n の型は、あたかも反復変数に代入されたかのように決定されます。具体的には、反復変数が既存のものである場合、反復値の型は反復変数の型であり、整数型でなければなりません。そうでない場合、反復変数が "range" 句によって宣言されているか、または存在しない場合、反復値の型は nデフォルト型です。n <= 0 の場合、ループは一切実行されません。
  6. 関数fの場合、イテレーションは、新しい合成されたyield関数を引数としてfを呼び出すことによって進行します。fが戻る前にyieldが呼び出されると、yieldへの引数はループ本体を一度実行するためのイテレーション値になります。連続する各ループイテレーションの後、yieldはtrueを返し、ループを続行するために再度呼び出すことができます。ループ本体が終了しない限り、"range"句はfが戻るまで、各yield呼び出しに対してこのようにイテレーション値を生成し続けます。ループ本体が終了する場合(例えばbreakステートメントによって)、yieldはfalseを返し、再度呼び出すことはできません。

イテレーション変数は、短い変数宣言:=)の形式を使用して"range"句によって宣言することができます。この場合、そのスコープは"for"ステートメントのブロックであり、各イテレーションは独自の新しい変数 [Go 1.22] を持ちます(ForClauseを持つ"for"ステートメントも参照)。変数はそれぞれのイテレーション値の型を持ちます。

イテレーション変数が "range" 句によって明示的に宣言されていない場合、それらは既存のものでなければなりません。この場合、イテレーション値は代入ステートメントのように対応する変数に代入されます。

var testdata *struct {
	a *[7]int
}
for i, _ := range testdata.a {
	// testdata.a is never evaluated; len(testdata.a) is constant
	// i ranges from 0 to 6
	f(i)
}

var a [10]string
for i, s := range a {
	// type of i is int
	// type of s is string
	// s == a[i]
	g(i, s)
}

var key string
var val interface{}  // element type of m is assignable to val
m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6}
for key, val = range m {
	h(key, val)
}
// key == last map key encountered in iteration
// val == map[key]

var ch chan Work = producer()
for w := range ch {
	doWork(w)
}

// empty a channel
for range ch {}

// call f(0), f(1), ... f(9)
for i := range 10 {
	// type of i is int (default type for untyped constant 10)
	f(i)
}

// invalid: 256 cannot be assigned to uint8
var u uint8
for u = range 256 {
}

// invalid: 1e3 is a floating-point constant
for range 1e3 {
}

// fibo generates the Fibonacci sequence
fibo := func(yield func(x int) bool) {
	f0, f1 := 0, 1
	for yield(f0) {
		f0, f1 = f1, f0+f1
	}
}

// print the Fibonacci numbers below 1000:
for x := range fibo {
	if x >= 1000 {
		break
	}
	fmt.Printf("%d ", x)
}
// output: 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987

// iteration support for a recursive tree data structure
type Tree[K cmp.Ordered, V any] struct {
	left, right *Tree[K, V]
	key         K
	value       V
}

func (t *Tree[K, V]) walk(yield func(key K, val V) bool) bool {
	return t == nil || t.left.walk(yield) && yield(t.key, t.value) && t.right.walk(yield)
}

func (t *Tree[K, V]) Walk(yield func(key K, val V) bool) {
	t.walk(yield)
}

// walk tree t in-order
var t Tree[string, int]
for k, v := range t.Walk {
	// process k, v
}

レンジ式の型が型パラメータである場合、その型集合内のすべての型は同じ基底型を持たなければならず、レンジ式はその型に対して有効でなければなりません。あるいは、型集合にチャネル型が含まれる場合、チャネル型のみが含まれていなければならず、それらはすべて同一の要素型を持たなければならず、チャネルの方向が衝突してはなりません。

Go ステートメント

"go" ステートメントは、同じアドレス空間内で関数呼び出しを独立した並行制御スレッド、つまりゴルーチンとして実行を開始します。

GoStmt = "go" Expression .

式は関数またはメソッド呼び出しでなければならず、括弧で囲むことはできません。組み込み関数の呼び出しは、式ステートメントと同様に制限されます。

関数値とパラメータは、呼び出し元のゴルーチンで通常通り評価されますが、通常の呼び出しとは異なり、プログラム実行は呼び出された関数が完了するのを待ちません。代わりに、関数は新しいゴルーチンで独立して実行を開始します。関数が終了すると、そのゴルーチンも終了します。関数に返り値がある場合、関数が完了するとそれらは破棄されます。

go Server()
go func(ch chan<- bool) { for { sleep(10); ch <- true }} (c)

Select ステートメント

"select" ステートメントは、可能な送信または受信操作のセットから、どれを進めるかを選択します。"switch" ステートメントと似ていますが、ケースがすべて通信操作を参照している点が異なります。

SelectStmt = "select" "{" { CommClause } "}" .
CommClause = CommCase ":" StatementList .
CommCase   = "case" ( SendStmt | RecvStmt ) | "default" .
RecvStmt   = [ ExpressionList "=" | IdentifierList ":=" ] RecvExpr .
RecvExpr   = Expression .

RecvStmt を持つケースは、RecvExpr の結果を1つまたは2つの変数に代入することができ、それらの変数は短い変数宣言を使用して宣言できます。RecvExpr は(括弧で囲むことも可能な)受信操作でなければなりません。デフォルトケースは最大1つまでしか存在せず、ケースのリストのどこにでも現れることができます。

「select」ステートメントの実行はいくつかのステップで進行します。

  1. ステートメント内のすべてのケースについて、受信操作のチャネルオペランドと、送信ステートメントのチャネルおよび右辺式は、「select」ステートメントに入るときに、ソース順に厳密に一度だけ評価されます。結果は、受信または送信するチャネルのセットと、送信する対応する値です。その評価における副作用は、どの通信操作が選択されて進行するか(もしあれば)に関わらず発生します。短い変数宣言または代入を含むRecvStmtの左辺の式はまだ評価されません。
  2. 1つ以上の通信が進行できる場合、進行できる単一の通信が均一な疑似乱数選択によって選ばれます。それ以外の場合、デフォルトケースがある場合はそのケースが選ばれます。デフォルトケースがない場合、「select」ステートメントは、少なくとも1つの通信が進行できるようになるまでブロックされます。
  3. 選択されたケースがデフォルトケースでない限り、それぞれの通信操作が実行されます。
  4. 選択されたケースが、短い変数宣言または代入を含む RecvStmt である場合、左辺の式が評価され、受信された値(または複数の値)が代入されます。
  5. 選択されたケースのステートメントリストが実行されます。

nilチャネルでの通信は決して進行しないため、nilチャネルのみでデフォルトケースがないselectは永遠にブロックされます。

var a []int
var c, c1, c2, c3, c4 chan int
var i1, i2 int
select {
case i1 = <-c1:
	print("received ", i1, " from c1\n")
case c2 <- i2:
	print("sent ", i2, " to c2\n")
case i3, ok := (<-c3):  // same as: i3, ok := <-c3
	if ok {
		print("received ", i3, " from c3\n")
	} else {
		print("c3 is closed\n")
	}
case a[f()] = <-c4:
	// same as:
	// case t := <-c4
	//	a[f()] = t
default:
	print("no communication\n")
}

for {  // send random sequence of bits to c
	select {
	case c <- 0:  // note: no statement, no fallthrough, no folding of cases
	case c <- 1:
	}
}

select {}  // block forever

Return ステートメント

関数F内の "return" ステートメントは、Fの実行を終了し、オプションで1つ以上の結果値を提供します。Fによって遅延されたすべての関数は、Fが呼び出し元に戻る前に実行されます。

ReturnStmt = "return" [ ExpressionList ] .

結果型を持たない関数では、"return" ステートメントは結果値を指定してはなりません。

func noResult() {
	return
}

結果型を持つ関数から値を返す方法は3つあります。

  1. 戻り値は"return" ステートメントで明示的にリストすることができます。各式は単一値であり、関数の結果型の対応する要素に代入可能でなければなりません。
    func simpleF() int {
    	return 2
    }
    
    func complexF1() (re float64, im float64) {
    	return -7.0, -4.0
    }
    
  2. 「return」ステートメントの式リストは、多値関数の単一の呼び出しであってもよい。その関数から返された各値が、それぞれの値の型を持つ一時変数に代入され、その後にこれらの変数をリストする「return」ステートメントが続くかのように効果があり、この時点で前のケースのルールが適用される。
    func complexF2() (re float64, im float64) {
    	return complexF1()
    }
    
  3. 関数の結果型が結果パラメータの名前を指定している場合、式リストは空であってもよい。結果パラメータは通常のローカル変数として機能し、関数は必要に応じてそれらに値を代入できる。"return" ステートメントはこれらの変数の値を返す。
    func complexF3() (re float64, im float64) {
    	re = 7.0
    	im = 4.0
    	return
    }
    
    func (devnull) Write(p []byte) (n int, _ error) {
    	n = len(p)
    	return
    }
    

どのように宣言されたかに関わらず、すべての結果値は関数へのエントリー時にその型のゼロ値に初期化されます。結果を指定する "return" ステートメントは、遅延関数が実行される前に結果パラメータを設定します。

実装上の制限:結果パラメータと同じ名前の別のエンティティ(定数、型、または変数)がreturnの位置でスコープ内にある場合、コンパイラは"return"ステートメント内の空の式リストを許可しない場合があります。

func f(n int) (res int, err error) {
	if _, err := f(n-1); err != nil {
		return  // invalid return statement: err is shadowed
	}
	return
}

Break ステートメント

"break" ステートメントは、同じ関数内の最も内側の"for""switch"、または"select"ステートメントの実行を終了します。

BreakStmt = "break" [ Label ] .

ラベルがある場合、それは囲んでいる "for"、"switch"、または "select" ステートメントのものでなければならず、その実行が終了します。

OuterLoop:
	for i = 0; i < n; i++ {
		for j = 0; j < m; j++ {
			switch a[i][j] {
			case nil:
				state = Error
				break OuterLoop
			case item:
				state = Found
				break OuterLoop
			}
		}
	}

Continue ステートメント

"continue" ステートメントは、最も内側の囲んでいる"for" ループの次のイテレーションを開始し、制御をループブロックの終わりに進めます。"for" ループは同じ関数内になければなりません。

ContinueStmt = "continue" [ Label ] .

ラベルがある場合、それは囲んでいる"for"ステートメントのものでなければならず、その実行が進められます。

RowLoop:
	for y, row := range rows {
		for x, data := range row {
			if data == endOfRow {
				continue RowLoop
			}
			row[x] = data + bias(x, y)
		}
	}

Goto ステートメント

"goto" ステートメントは、同じ関数内の対応するラベルを持つステートメントに制御を移します。

GotoStmt = "goto" Label .
goto Error

"goto" ステートメントの実行は、goto の時点で既にスコープ内にない変数をスコープ内に入れるべきではありません。例えば、この例

	goto L  // BAD
	v := 3
L:

は、ラベルLへのジャンプがvの作成をスキップするため、誤りです。

ブロック外の"goto"ステートメントは、そのブロック内のラベルにジャンプすることはできません。例えば、この例

if n%2 == 1 {
	goto L1
}
for n > 0 {
	f()
	n--
L1:
	f()
	n--
}

は、ラベルL1が"for"ステートメントのブロック内にあるにもかかわらず、gotoがそうでないため、誤りです。

Fallthrough ステートメント

"fallthrough" ステートメントは、式 "switch" ステートメント内の次のケース句の最初のステートメントに制御を移します。この句の最後の空ではないステートメントとしてのみ使用できます。

FallthroughStmt = "fallthrough" .

Defer ステートメント

"defer" ステートメントは、周囲の関数がreturn ステートメントを実行したか、関数本体の終わりに達したか、または対応するゴルーチンがパニックを起こしているために、周囲の関数が戻る瞬間に実行が遅延される関数を呼び出します。

DeferStmt = "defer" Expression .

式は関数またはメソッド呼び出しでなければならず、括弧で囲むことはできません。組み込み関数の呼び出しは、式ステートメントと同様に制限されます。

"defer" ステートメントが実行されるたびに、関数値と呼び出しへのパラメータは通常通り評価され、新たに保存されますが、実際の関数は呼び出されません。代わりに、遅延された関数は、周囲の関数が戻る直前に、遅延された順序の逆で呼び出されます。つまり、周囲の関数が明示的なreturn ステートメントによって戻る場合、遅延された関数は、そのreturn ステートメントによって結果パラメータが設定されたに実行されますが、関数が呼び出し元に戻るに実行されます。遅延された関数値がnilと評価される場合、"defer" ステートメントが実行されたときではなく、関数が呼び出されたときに実行がパニックを起こします。

たとえば、遅延関数が関数リテラルであり、周囲の関数がリテラル内でスコープ内にある名前付き結果パラメータを持っている場合、遅延関数はそれらが返される前に結果パラメータにアクセスして変更することができます。遅延関数が戻り値を持っている場合、関数が完了するとそれらは破棄されます。(パニックの処理に関するセクションも参照してください。)

lock(l)
defer unlock(l)  // unlocking happens before surrounding function returns

// prints 3 2 1 0 before surrounding function returns
for i := 0; i <= 3; i++ {
	defer fmt.Print(i)
}

// f returns 42
func f() (result int) {
	defer func() {
		// result is accessed after it was set to 6 by the return statement
		result *= 7
	}()
	return 6
}

組み込み関数

組み込み関数は事前宣言されています。他の関数と同様に呼び出されますが、中には最初の引数として式ではなく型を受け取るものもあります。

組み込み関数には標準の Go 型がないため、呼び出し式にのみ現れることができます。関数値として使用することはできません。

スライスの追加とコピー

組み込み関数appendcopyは、一般的なスライス操作を助けます。どちらの関数についても、結果は引数によって参照されるメモリが重複するかどうかに関係なく独立しています。

可変長引数関数appendは、型Sのスライスsにゼロ個以上の値xを追加し、結果のスライス(これも型S)を返します。値xは型...Eのパラメータに渡され、ここでESの要素型であり、対応するパラメータ渡し規則が適用されます。特別なケースとして、appendは、型[]byteに代入可能な最初の引数と、文字列型で...が続く2番目の引数も受け入れます。この形式は文字列のバイトを追加します。

append(s S, x ...E) S  // E is the element type of S

S型パラメータである場合、その型セット内のすべての型は同じ基底のスライス型[]Eを持たなければなりません。

sの容量が追加の値を収めるのに十分でない場合、appendは既存のスライス要素と追加の両方を収めるのに十分な、新しい基底配列を割り当てます。そうでない場合、appendは基底配列を再利用します。

s0 := []int{0, 0}
s1 := append(s0, 2)                // append a single element     s1 is []int{0, 0, 2}
s2 := append(s1, 3, 5, 7)          // append multiple elements    s2 is []int{0, 0, 2, 3, 5, 7}
s3 := append(s2, s0...)            // append a slice              s3 is []int{0, 0, 2, 3, 5, 7, 0, 0}
s4 := append(s3[3:6], s3[2:]...)   // append overlapping slice    s4 is []int{3, 5, 7, 2, 3, 5, 7, 0, 0}

var t []interface{}
t = append(t, 42, 3.1415, "foo")   //                             t is []interface{}{42, 3.1415, "foo"}

var b []byte
b = append(b, "bar"...)            // append string contents      b is []byte{'b', 'a', 'r' }

関数copyは、ソースsrcから宛先dstにスライス要素をコピーし、コピーされた要素の数を返します。両方の引数は同一の要素型Eを持ち、型[]Eのスライスに代入可能でなければなりません。コピーされる要素の数はlen(src)len(dst)の小さい方です。特別なケースとして、copyは、型[]byteに代入可能な宛先引数と、string型のソース引数も受け入れます。この形式は、文字列からバイトスライスにバイトをコピーします。

copy(dst, src []T) int
copy(dst []byte, src string) int

1つまたは両方の引数の型が型パラメータである場合、それぞれの型集合内のすべての型は同じ基底のスライス型[]Eを持たなければなりません。

var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
var s = make([]int, 6)
var b = make([]byte, 5)
n1 := copy(s, a[0:])            // n1 == 6, s is []int{0, 1, 2, 3, 4, 5}
n2 := copy(s, s[2:])            // n2 == 4, s is []int{2, 3, 4, 5, 4, 5}
n3 := copy(b, "Hello, World!")  // n3 == 5, b is []byte("Hello")

クリア

組み込み関数clearは、マップスライス、または型パラメータ型の引数を取り、すべての要素を削除またはゼロクリアします [Go 1.21]。

Call        Argument type     Result

clear(m)    map[K]T           deletes all entries, resulting in an
                              empty map (len(m) == 0)

clear(s)    []T               sets all elements up to the length of
                              s to the zero value of T

clear(t)    type parameter    see below

clearの引数の型が型パラメータである場合、その型集合内のすべての型はマップまたはスライスでなければならず、clearは実際の型引数に対応する操作を実行します。

マップまたはスライスがnilの場合、clearは何もしません。

閉じる

チャネルchの場合、組み込み関数close(ch)は、チャネルにこれ以上値が送信されないことを記録します。chが受信専用チャネルの場合、エラーになります。クローズされたチャネルへの送信またはクローズは実行時パニックを引き起こします。nilチャネルをクローズすることも実行時パニックを引き起こします。closeを呼び出した後、以前に送信された値がすべて受信された後、受信操作はチャネルの型のゼロ値をブロックせずに返します。多値受信操作は、受信された値と、チャネルがクローズされているかどうかの指示を返します。

closeの引数の型が型パラメータである場合、その型集合内のすべての型は同じ要素型を持つチャネルでなければなりません。それらのチャネルのいずれかが受信専用チャネルである場合、エラーになります。

複素数の操作

3つの関数が複素数を組み立てたり分解したりします。組み込み関数complexは浮動小数点の実数部と虚数部から複素数値を構築し、一方realimagは複素数値の実数部と虚数部を抽出します。

complex(realPart, imaginaryPart floatT) complexT
real(complexT) floatT
imag(complexT) floatT

引数と戻り値の型は対応しています。complexの場合、2つの引数は同じ浮動小数点型でなければならず、戻り値の型は対応する浮動小数点構成要素を持つ複素数型です。float32引数にはcomplex64float64引数にはcomplex128です。引数の1つが型なし定数と評価される場合、まず暗黙的に他の引数の型に変換されます。両方の引数が型なし定数と評価される場合、それらは非複素数であるか、または虚数部がゼロでなければならず、関数の戻り値は型なし複素数定数になります。

realimagの場合、引数は複素数型でなければならず、戻り値の型は対応する浮動小数点型です。complex64引数にはfloat32complex128引数にはfloat64です。引数が型なし定数と評価される場合、それは数値でなければならず、関数の戻り値は型なし浮動小数点定数になります。

real関数とimag関数はcomplexの逆関数を構成するため、複素数型Zの値zについて、z == Z(complex(real(z), imag(z)))となります。

これらの関数のオペランドがすべて定数である場合、戻り値は定数です。

var a = complex(2, -2)             // complex128
const b = complex(1.0, -1.4)       // untyped complex constant 1 - 1.4i
x := float32(math.Cos(math.Pi/2))  // float32
var c64 = complex(5, -x)           // complex64
var s int = complex(1, 0)          // untyped complex constant 1 + 0i can be converted to int
_ = complex(1, 2<<s)               // illegal: 2 assumes floating-point type, cannot shift
var rl = real(c64)                 // float32
var im = imag(a)                   // float64
const c = imag(b)                  // untyped constant -1.4
_ = imag(3 << s)                   // illegal: 3 assumes complex type, cannot shift

型パラメータ型の引数は許可されません。

マップ要素の削除

組み込み関数deleteは、キーkを持つ要素をマップmから削除します。値kmのキー型に代入可能でなければなりません。

delete(m, k)  // remove element m[k] from map m

mの型が型パラメータである場合、その型集合内のすべての型はマップでなければならず、それらはすべて同一のキー型を持たなければなりません。

マップmnilであるか、または要素m[k]が存在しない場合、deleteは何も実行しません。

長さと容量

組み込み関数lencapは、様々な型の引数を取り、型intの結果を返します。実装は、結果が常にintに収まることを保証します。

Call      Argument type    Result

len(s)    string type      string length in bytes
          [n]T, *[n]T      array length (== n)
          []T              slice length
          map[K]T          map length (number of defined keys)
          chan T           number of elements queued in channel buffer
          type parameter   see below

cap(s)    [n]T, *[n]T      array length (== n)
          []T              slice capacity
          chan T           channel buffer capacity
          type parameter   see below

引数型が型パラメータ P である場合、len(e) (またはcap(e)) の呼び出しは、P の型セット内の各型に対して有効でなければなりません。結果は、Pインスタンス化された型引数に対応する引数の長さ (または容量) です。

スライスの容量は、基底配列にスペースが割り当てられている要素の数です。常に以下の関係が成り立ちます。

0 <= len(s) <= cap(s)

nil スライス、マップ、またはチャネルの長さは 0 です。nil スライスまたはチャネルの容量は 0 です。

len(s)は、sが文字列定数の場合、定数です。式len(s)cap(s)は、sの型が配列または配列へのポインタであり、かつsチャネル受信または(非定数の)関数呼び出しを含まない場合、定数です。この場合、sは評価されません。それ以外の場合、lencapの呼び出しは定数ではなく、sは評価されます。

const (
	c1 = imag(2i)                    // imag(2i) = 2.0 is a constant
	c2 = len([10]float64{2})         // [10]float64{2} contains no function calls
	c3 = len([10]float64{c1})        // [10]float64{c1} contains no function calls
	c4 = len([10]float64{imag(2i)})  // imag(2i) is a constant and no function call is issued
	c5 = len([10]float64{imag(z)})   // invalid: imag(z) is a (non-constant) function call
)
var z complex128

スライス、マップ、チャネルの作成

組み込み関数makeは、スライス、マップ、チャネルの型、または型パラメータである型Tを引数に取り、オプションで型固有の式リストが続きます。型Tの値(*Tではありません)を返します。メモリは初期値のセクションで説明されているように初期化されます。

Call             Type T            Result

make(T, n)       slice             slice of type T with length n and capacity n
make(T, n, m)    slice             slice of type T with length n and capacity m

make(T)          map               map of type T
make(T, n)       map               map of type T with initial space for approximately n elements

make(T)          channel           unbuffered channel of type T
make(T, n)       channel           buffered channel of type T, buffer size n

make(T, n)       type parameter    see below
make(T, n, m)    type parameter    see below

最初の引数が型パラメータである場合、その型セット内のすべての型は同じ基底型を持たなければならず、その基底型はスライス型またはマップ型でなければなりません。あるいは、チャネル型がある場合、チャネル型のみが存在しなければならず、それらはすべて同じ要素型を持たなければならず、チャネルの方向は競合してはなりません。

サイズ引数nmはそれぞれ、整数型であるか、整数型のみを含む型集合を持つか、型なし定数である必要があります。定数のサイズ引数は負ではない表現可能int型の値である必要があります。型なし定数の場合はint型が与えられます。nmの両方が与えられ、かつ定数の場合、nmより大きくない必要があります。スライスとチャネルの場合、実行時にnが負またはmより大きい場合、実行時パニックが発生します。

s := make([]int, 10, 100)       // slice with len(s) == 10, cap(s) == 100
s := make([]int, 1e3)           // slice with len(s) == cap(s) == 1000
s := make([]int, 1<<63)         // illegal: len(s) is not representable by a value of type int
s := make([]int, 10, 0)         // illegal: len(s) > cap(s)
c := make(chan int, 10)         // channel with a buffer size of 10
m := make(map[string]int, 100)  // map with initial space for approximately 100 elements

マップ型とサイズヒントnmakeを呼び出すと、n個のマップ要素を保持する初期スペースを持つマップが作成されます。正確な動作は実装に依存します。

最小値と最大値

組み込み関数minmaxは、固定数の順序付き型の引数について、それぞれ最小値または最大値を計算します。引数は少なくとも1つ必要です [Go 1.21]。

演算子と同じ型ルールが適用されます。順序付き引数xyの場合、x + yが有効であればmin(x, y)も有効であり、min(x, y)の型はx + yの型と同じです(maxも同様)。すべての引数が定数であれば、結果も定数です。

var x, y int
m := min(x)                 // m == x
m := min(x, y)              // m is the smaller of x and y
m := max(x, y, 10)          // m is the larger of x and y but at least 10
c := max(1, 2.0, 10)        // c == 10.0 (floating-point kind)
f := max(0, float32(x))     // type of f is float32
var s []string
_ = min(s...)               // invalid: slice arguments are not permitted
t := max("", "foo", "bar")  // t == "foo" (string kind)

数値引数の場合、すべてのNaNが等しいと仮定すると、minmaxは可換で結合的です。

min(x, y)    == min(y, x)
min(x, y, z) == min(min(x, y), z) == min(x, min(y, z))

浮動小数点引数の負のゼロ、NaN、および無限大については、以下のルールが適用されます。

   x        y    min(x, y)    max(x, y)

  -0.0    0.0         -0.0          0.0    // negative zero is smaller than (non-negative) zero
  -Inf      y         -Inf            y    // negative infinity is smaller than any other number
  +Inf      y            y         +Inf    // positive infinity is larger than any other number
   NaN      y          NaN          NaN    // if any argument is a NaN, the result is a NaN

文字列引数の場合、minの結果は、字句的にバイト単位で比較して最小値(maxの場合は最大値)を持つ最初の引数です。

min(x, y)    == if x <= y then x else y
min(x, y, z) == min(min(x, y), z)

割り当て

組み込み関数newは、型Tを引数に取り、実行時にその型の変数のストレージを割り当て、それを指す*Tの値を返します。変数は初期値のセクションで説明されているように初期化されます。

new(T)

例えば

type S struct { a int; b float64 }
new(S)

Sの変数のストレージを割り当て、初期化し(a=0b=0.0)、その場所のアドレスを含む型*Sの値を返します。

パニックの処理

2つの組み込み関数panicrecoverは、実行時パニックやプログラム定義のエラー状況の報告と処理を支援します。

func panic(interface{})
func recover() interface{}

関数Fの実行中に、panicの明示的な呼び出しまたは実行時パニックFの実行を終了します。Fによって遅延された関数は通常通り実行されます。次に、Fの呼び出し元によって実行される遅延された関数が実行され、実行中のゴルーチンのトップレベル関数によって遅延された関数まで同様に実行されます。その時点でプログラムは終了し、panicへの引数の値を含むエラー状況が報告されます。この終了シーケンスはパニックと呼ばれます。

panic(42)
panic("unreachable")
panic(Error("cannot parse"))

recover関数は、パニック状態のゴルーチンの動作をプログラムが管理できるようにします。関数Grecoverを呼び出す関数Dを遅延し、Gが実行されているのと同じゴルーチン内の関数でパニックが発生したとします。遅延関数の実行がDに達すると、Drecover呼び出しの戻り値はpanicの呼び出しに渡された値になります。Dが新しいpanicを開始せずに正常に終了した場合、パニックシーケンスは停止します。この場合、Gpanic呼び出しの間に呼び出された関数の状態は破棄され、通常の実行が再開されます。Dの前にGによって遅延された関数はその後実行され、Gの実行は呼び出し元に戻ることによって終了します。

recoverの戻り値は、ゴルーチンがパニック状態でない場合、またはrecoverが遅延関数によって直接呼び出されなかった場合にnilになります。逆に、ゴルーチンがパニック状態で、recoverが遅延関数によって直接呼び出された場合、recoverの戻り値はnilでないことが保証されます。これを確実にするため、nilインターフェース値(または型なしnil)でpanicを呼び出すと、実行時パニックが発生します。

以下の例のprotect関数は、関数引数gを呼び出し、gによって引き起こされる実行時パニックから呼び出し元を保護します。

func protect(g func()) {
	defer func() {
		log.Println("done")  // Println executes normally even if there is a panic
		if x := recover(); x != nil {
			log.Printf("run time panic: %v", x)
		}
	}()
	log.Println("start")
	g()
}

ブートストラップ

現在の実装では、ブートストラップ中に役立ついくつかの組み込み関数が提供されています。これらの関数は完全性のために文書化されていますが、言語に残ることが保証されているわけではありません。結果は返しません。

Function   Behavior

print      prints all arguments; formatting of arguments is implementation-specific
println    like print but prints spaces between arguments and a newline at the end

実装上の制限: printprintlnは任意の引数型を受け入れる必要はありませんが、ブール、数値、および文字列の出力はサポートされる必要があります。

パッケージ

Goプログラムは、パッケージをリンクして構築されます。パッケージは、パッケージに属する定数、型、変数、関数を宣言する1つ以上のソースファイルから構成され、これらの要素は同じパッケージ内のすべてのファイルでアクセス可能です。これらの要素はエクスポートされ、他のパッケージで使用されることがあります。

ソースファイルの構成

各ソースファイルは、それに属するパッケージを定義するパッケージ句で始まり、続いて使用したいパッケージの内容を宣言する、空の場合もあるインポート宣言のセット、そして空の場合もある関数、型、変数、定数の宣言のセットが続きます。

SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .

パッケージ句

パッケージ句は各ソースファイルの先頭にあり、そのファイルが属するパッケージを定義します。

PackageClause = "package" PackageName .
PackageName   = identifier .

PackageNameはブランク識別子であってはなりません。

package math

同じPackageNameを共有するファイルのセットは、パッケージの実装を形成します。実装によっては、パッケージのすべてのソースファイルが同じディレクトリにあることを要求する場合があります。

インポート宣言

インポート宣言は、宣言を含むソースファイルがインポートされたパッケージの機能に依存し(§プログラムの初期化と実行)、そのパッケージのエクスポートされた識別子へのアクセスを可能にすることを表明します。インポートは、アクセスに使用する識別子(PackageName)と、インポートするパッケージを指定するImportPathに名前を付けます。

ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
ImportSpec = [ "." | PackageName ] ImportPath .
ImportPath = string_lit .

PackageNameは、インポートするソースファイル内でパッケージのエクスポートされた識別子にアクセスするために修飾識別子として使用されます。それはファイルブロックで宣言されます。PackageNameが省略された場合、それはインポートされたパッケージのパッケージ句で指定された識別子にデフォルト設定されます。名前の代わりに明示的なピリオド(.)がある場合、そのパッケージのパッケージブロックで宣言されたすべてのパッケージのエクスポートされた識別子は、インポートするソースファイルのファイルブロックで宣言され、修飾子なしでアクセスされなければなりません。

ImportPathの解釈は実装に依存しますが、通常はコンパイルされたパッケージの完全なファイル名のサブストリングであり、インストールされたパッケージのリポジトリに対する相対パスである場合があります。

実装上の制限: コンパイラはImportPathsを、UnicodeのL, M, N, P, Sの一般カテゴリ(スペースを含まないグラフィック文字)に属する文字のみを使用する空でない文字列に制限し、また文字!"#$%&'()*,:;<=>?[\]^`{|}およびUnicode置換文字U+FFFDを除外する場合があります。

パッケージ句package mathを含むコンパイル済みパッケージを考えます。このパッケージは関数Sinをエクスポートし、コンパイル済みパッケージをファイル"lib/math"で識別される場所にインストールしました。この表は、さまざまな種類のインポート宣言の後、パッケージをインポートするファイルでSinにどのようにアクセスするかを示しています。

Import declaration          Local name of Sin

import   "lib/math"         math.Sin
import m "lib/math"         m.Sin
import . "lib/math"         Sin

インポート宣言は、インポート元パッケージとインポート先パッケージ間の依存関係を宣言します。パッケージが直接的または間接的に自分自身をインポートしたり、エクスポートされた識別子のいずれも参照せずにパッケージを直接インポートしたりすることは不正です。副作用(初期化)のためだけにパッケージをインポートするには、明示的なパッケージ名としてブランク識別子を使用します。

import _ "lib/math"

パッケージの例

以下に、並行素数ふるいを実装する完全なGoパッケージを示します。

package main

import "fmt"

// Send the sequence 2, 3, 4, … to channel 'ch'.
func generate(ch chan<- int) {
	for i := 2; ; i++ {
		ch <- i  // Send 'i' to channel 'ch'.
	}
}

// Copy the values from channel 'src' to channel 'dst',
// removing those divisible by 'prime'.
func filter(src <-chan int, dst chan<- int, prime int) {
	for i := range src {  // Loop over values received from 'src'.
		if i%prime != 0 {
			dst <- i  // Send 'i' to channel 'dst'.
		}
	}
}

// The prime sieve: Daisy-chain filter processes together.
func sieve() {
	ch := make(chan int)  // Create a new channel.
	go generate(ch)       // Start generate() as a subprocess.
	for {
		prime := <-ch
		fmt.Print(prime, "\n")
		ch1 := make(chan int)
		go filter(ch, ch1, prime)
		ch = ch1
	}
}

func main() {
	sieve()
}

プログラムの初期化と実行

ゼロ値

宣言またはnewの呼び出しによって変数のストレージが割り当てられるとき、または複合リテラルまたはmakeの呼び出しによって新しい値が作成され、明示的な初期化が提供されない場合、変数または値にはデフォルト値が与えられます。そのような変数または値の各要素は、その型のゼロ値に設定されます。ブール型の場合はfalse、数値型の場合は0、文字列の場合は""、ポインタ、関数、インターフェース、スライス、チャネル、マップの場合はnilです。この初期化は再帰的に行われるため、例えば、値が指定されない場合、構造体の配列の各要素のフィールドはゼロクリアされます。

これら2つの単純な宣言は同等です。

var i int
var i int = 0

type T struct { i int; f float64; next *T }
t := new(T)

以下が成り立ちます。

t.i == 0
t.f == 0.0
t.next == nil

その後も同様です。

var t T

パッケージ初期化

パッケージ内では、パッケージレベル変数の初期化は段階的に進行し、各ステップで、初期化されていない変数に依存しない、宣言順序で最も早い変数が選択されます。

より正確には、パッケージレベル変数は、まだ初期化されておらず、かつ初期化式がないか、またはその初期化式が未初期化変数への依存関係を持たない場合に、初期化準備完了と見なされます。初期化は、宣言順序で最も早く、初期化準備完了である次のパッケージレベル変数を繰り返し初期化することによって進行し、初期化準備完了の変数がなくなるまで続きます。

このプロセスが終了した時点でまだ初期化されていない変数が残っている場合、それらの変数は1つまたは複数の初期化サイクルの一部であり、プログラムは無効です。

変数宣言の左辺にある複数の変数が、右辺の単一の(多値)式によって初期化される場合、それらはまとめて初期化されます。左辺の変数のいずれかが初期化される場合、それらのすべての変数は同じステップで初期化されます。

var x = a
var a, b = f() // a and b are initialized together, before x is initialized

パッケージ初期化の目的では、ブランク変数は宣言内の他の変数と同様に扱われます。

複数のファイルで宣言された変数の宣言順序は、ファイルがコンパイラに提示される順序によって決定されます。最初のファイルで宣言された変数は、2番目のファイルで宣言された変数よりも前に宣言され、以下同様です。再現可能な初期化動作を保証するため、ビルドシステムは同じパッケージに属する複数のファイルを字句ファイル名順にコンパイラに提示することが推奨されます。

依存関係分析は、変数の実際の値には依存せず、ソース内のそれらへの字句的な参照にのみ依存し、推移的に分析されます。例えば、変数xの初期化式が、その本体が変数yを参照する関数を参照する場合、xyに依存します。具体的には、

  • 変数または関数への参照は、その変数または関数を示す識別子です。
  • メソッドmへの参照は、形式t.mメソッド値またはメソッド式であり、tの(静的)型はインターフェース型ではなく、メソッドmtメソッドセット内に存在します。結果の関数値t.mが呼び出されるかどうかは重要ではありません。
  • 変数、関数、またはメソッドxは、xの初期化式または本体(関数とメソッドの場合)がyへの参照、またはyに依存する関数またはメソッドへの参照を含む場合、変数yに依存します。

たとえば、宣言が与えられた場合

var (
	a = c + b  // == 9
	b = f()    // == 4
	c = f()    // == 5
	d = 3      // == 5 after initialization has finished
)

func f() int {
	d++
	return d
}

初期化順序はdbcaです。初期化式内のサブ式の順序は関係ないことに注意してください。この例では、a = c + ba = b + cは同じ初期化順序になります。

依存関係分析はパッケージごとに行われます。現在のパッケージで宣言された変数、関数、および(非インターフェース)メソッドへの参照のみが考慮されます。変数間に他の隠れたデータ依存関係が存在する場合、それらの変数間の初期化順序は未指定です。

例えば、以下の宣言が与えられた場合

var x = I(T{}).ab()   // x has an undetected, hidden dependency on a and b
var _ = sideEffect()  // unrelated to x, a, or b
var a = b
var b = 42

type I interface      { ab() []int }
type T struct{}
func (T) ab() []int   { return []int{a, b} }

変数abの後に初期化されますが、xbの前に初期化されるか、baの間に初期化されるか、またはaの後に初期化されるか、したがってsideEffect()が呼び出されるタイミング(xが初期化される前か後か)は指定されていません。

変数も、パッケージブロックで宣言された、引数と結果パラメータを持たないinitという名前の関数を使用して初期化することができます。

func init() { … }

そのような関数は、単一のソースファイル内でも、パッケージごとに複数定義することができます。パッケージブロックでは、init識別子はinit関数を宣言するためにのみ使用できますが、識別子自体は宣言されません。したがって、init関数はプログラムのどこからも参照することはできません。

パッケージ全体は、すべてのパッケージレベル変数に初期値を割り当てた後、ソースに現れる順序(複数のファイルにまたがる場合もある)で、コンパイラに提示された順序で、すべてのinit関数を呼び出すことによって初期化されます。

プログラムの初期化

完全なプログラムのパッケージは、一度に1つのパッケージずつ、段階的に初期化されます。パッケージがインポートを持つ場合、インポートされたパッケージはパッケージ自体を初期化する前に初期化されます。複数のパッケージが1つのパッケージをインポートする場合、インポートされたパッケージは一度だけ初期化されます。パッケージのインポートは、構造上、循環初期化依存関係がないことを保証します。より正確には、

すべてのパッケージのリストをインポートパスでソートして与えられた場合、各ステップで、リスト内の最初の未初期化パッケージのうち、インポートされたすべてのパッケージ(もしあれば)が既に初期化されているものが初期化されます。このステップは、すべてのパッケージが初期化されるまで繰り返されます。

パッケージの初期化(変数の初期化とinit関数の呼び出し)は、1つのゴルーチンで、一度に1つのパッケージずつ順次行われます。init関数は他のゴルーチンを起動することができ、それらは初期化コードと並行して実行できます。ただし、初期化は常にinit関数の順序付けを行います。前のinit関数が戻るまで次の関数は呼び出されません。

プログラムの実行

完全なプログラムは、メインパッケージと呼ばれる単一のインポートされていないパッケージと、それが推移的にインポートするすべてのパッケージをリンクすることによって作成されます。メインパッケージは、パッケージ名mainを持ち、引数を取らず値を返さない関数mainを宣言する必要があります。

func main() { … }

プログラムの実行は、プログラムを初期化し、次にパッケージmainの関数mainを呼び出すことで始まります。その関数呼び出しが戻ると、プログラムは終了します。他の(main以外の)ゴルーチンが完了するのを待ちません。

エラー

事前宣言された型errorは次のように定義されます。

type error interface {
	Error() string
}

これはエラー状態を表すための慣習的なインターフェースであり、nil値はエラーがないことを表します。例えば、ファイルからデータを読み込む関数は次のように定義できます。

func Read(f *File, b []byte) (n int, err error)

実行時パニック

配列の範囲外へのアクセスを試みるなどの実行時エラーは、組み込み関数panicを実装定義のインターフェース型runtime.Errorの値で呼び出すのと同等の実行時パニックを引き起こします。この型は、事前宣言されたインターフェース型errorを満たします。異なる実行時エラー条件を表す正確なエラー値は指定されていません。

package runtime

type Error interface {
	error
	// and perhaps other methods
}

システムの考慮事項

パッケージunsafe

コンパイラに認識され、インポートパス"unsafe"を通じてアクセス可能な組み込みパッケージunsafeは、型システムに違反する操作を含む低レベルプログラミングの機能を提供します。unsafeを使用するパッケージは、型の安全性について手動で検査する必要があり、移植性がない場合があります。このパッケージは以下のインターフェースを提供します。

package unsafe

type ArbitraryType int  // shorthand for an arbitrary Go type; it is not a real type
type Pointer *ArbitraryType

func Alignof(variable ArbitraryType) uintptr
func Offsetof(selector ArbitraryType) uintptr
func Sizeof(variable ArbitraryType) uintptr

type IntegerType int  // shorthand for an integer type; it is not a real type
func Add(ptr Pointer, len IntegerType) Pointer
func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType
func SliceData(slice []ArbitraryType) *ArbitraryType
func String(ptr *byte, len IntegerType) string
func StringData(str string) *byte

Pointerポインタ型ですが、Pointer値は間接参照できません。任意のポインタまたは基底型uintptrの値は、基底型Pointerの型に変換でき、その逆も可能です。対応する型が型パラメータである場合、それぞれの型集合内のすべての型は、それぞれuintptrPointerでなければならない同じ基底型を持たなければなりません。Pointeruintptr間の変換の効果は実装定義です。

var f float64
bits = *(*uint64)(unsafe.Pointer(&f))

type ptr unsafe.Pointer
bits = *(*uint64)(ptr(&f))

func f[P ~*B, B any](p P) uintptr {
	return uintptr(unsafe.Pointer(p))
}

var p ptr = nil

関数AlignofSizeofは、任意の型の式xを受け取り、あたかもvar v = xによって宣言されたかのような仮想的な変数vの、それぞれアラインメントまたはサイズを返します。

関数Offsetofは、(括弧で囲むことも可能な)セレクタs.fを受け取ります。これは、sまたは*sによって示される構造体のフィールドfを示し、構造体のアドレスに対するフィールドのバイトオフセットを返します。f組み込みフィールドの場合、構造体のフィールドを介したポインタ間接参照なしで到達可能でなければなりません。フィールドfを持つ構造体sの場合、

uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f) == uintptr(unsafe.Pointer(&s.f))

コンピュータアーキテクチャによってはメモリのアドレスがアラインメントされている必要があります。つまり、変数のアドレスは係数の倍数でなければならず、それが変数の型のアラインメントです。関数Alignofは任意の型の変数を表す式を受け取り、その変数(の型)のバイト単位のアラインメントを返します。変数xの場合、

uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0

T型パラメータである場合、またはサイズがゼロより大きいフィールド(または要素)を含む配列型または構造体型である場合、その型は可変サイズを持ちます。そうでなければサイズは定数です。AlignofOffsetofSizeofの呼び出しは、引数(またはOffsetofのセレクタ式s.fにおける構造体s)が定数サイズの型である場合、コンパイル時定数式(型はuintptr)となります。

関数Addlenptrに加算し、更新されたポインタunsafe.Pointer(uintptr(ptr) + uintptr(len))を返します [Go 1.17]。len引数は整数型または型なし定数でなければなりません。定数のlen引数は、int型の値によって表現可能でなければなりません。型なし定数である場合、int型が与えられます。Pointer有効な使用法の規則は引き続き適用されます。

関数Sliceは、基底配列がptrから始まり、長さと容量がlenであるスライスを返します。Slice(ptr, len)は、

(*[len]ArbitraryType)(unsafe.Pointer(ptr))[:]

と等価ですが、特別なケースとして、ptrnillenがゼロの場合、Slicenilを返します [Go 1.17]。

len引数は整数型または型なし定数でなければなりません。定数len引数は非負で、int型の値によって表現可能でなければなりません。型なし定数である場合、int型が与えられます。実行時にlenが負の場合、またはptrnillenがゼロでない場合、実行時パニックが発生します [Go 1.17]。

関数SliceDataは、slice引数の基底配列へのポインタを返します。スライスの容量cap(slice)がゼロでない場合、そのポインタは&slice[:1][0]です。slicenilの場合、結果はnilです。それ以外の場合は、未指定のメモリアドレスへの非nilポインタです [Go 1.20]。

関数Stringは、基底バイトがptrから始まり、長さがlenであるstring値を返します。ptrlen引数には関数Sliceと同じ要件が適用されます。lenがゼロの場合、結果は空文字列""です。Goの文字列は不変であるため、Stringに渡されたバイトは後で変更してはなりません。 [Go 1.20]

関数StringDataは、str引数の基底バイトへのポインタを返します。空文字列の場合、戻り値は未指定であり、nilである可能性があります。Goの文字列は不変であるため、StringDataによって返されるバイトは変更してはなりません [Go 1.20]。

サイズとアライメントの保証

数値型の場合、以下のサイズが保証されます。

type                                 size in bytes

byte, uint8, int8                     1
uint16, int16                         2
uint32, int32, float32                4
uint64, int64, float64, complex64     8
complex128                           16

以下の最小アライメント特性が保証されます。

  1. 任意の型の変数xの場合:unsafe.Alignof(x)は少なくとも1です。
  2. 構造体型の変数xの場合:unsafe.Alignof(x)は、xの各フィールドfに対するunsafe.Alignof(x.f)のすべての値のうち最大のものですが、少なくとも1です。
  3. 配列型の変数xの場合:unsafe.Alignof(x)は、配列の要素型の変数と同じアラインメントです。

構造体型または配列型は、サイズがゼロより大きいフィールド(または要素)を一つも含まない場合、サイズゼロを持ちます。異なる2つのサイズゼロ変数は、メモリ内で同じアドレスを持つ場合があります。

付録

言語バージョン

Go 1 の互換性保証は、Go 1 仕様に準拠して書かれたプログラムが、その仕様の存続期間中、変更なしで正しくコンパイルおよび実行され続けることを保証します。より一般的には、言語に調整や機能が追加される際、互換性保証は、特定の Go 言語バージョンで動作する Go プログラムが、その後のすべてのバージョンで動作し続けることを保証します。

例えば、バイナリ整数リテラルのプリフィックス0bを使用する機能はGo 1.13で導入され、整数リテラルのセクションで [Go 1.13] と示されています。0b1011のような整数リテラルを含むソースコードは、コンパイラによって使用される暗黙的または必須の言語バージョンがGo 1.13より古い場合、拒否されます。

以下の表は、Go 1以降に導入された機能に必要な最小言語バージョンを示しています。

Go 1.9

Go 1.13

  • 整数リテラルは、バイナリと8進リテラルにそれぞれプリフィックス0b0B0o0Oを使用できます。
  • 16進数の浮動小数点リテラルは、プリフィックス0x0Xを使用して記述できます。
  • 虚数サフィックスiは、10進数リテラルだけでなく、任意の(バイナリ、10進数、16進数)整数または浮動小数点リテラルで使用できます。
  • 任意の数値リテラルの桁は、アンダースコア_を使用して区切る(グループ化する)ことができます。
  • シフト操作のシフトカウントは、符号付き整数型であってもよい。

Go 1.14

Go 1.17

  • スライスと配列の要素型が一致し、配列がスライスより長くない場合、スライスを配列ポインタに変換できます。
  • 組み込みのパッケージunsafeには、新しい関数AddSliceが含まれています。

Go 1.18

Go 1.18リリースでは、ポリモーフィックな関数と型(「ジェネリクス」)が言語に追加されます。具体的には、

  • 演算子と句読点のセットには、新しいトークン~が含まれます。
  • 関数および型宣言は型パラメータを宣言できます。
  • インターフェース型は、任意の型を埋め込む(インターフェースの型名だけでなく)ことができ、また共用体と~T型要素も埋め込むことができます。
  • 事前宣言された型のセットには、新しい型anycomparableが含まれます。

Go 1.20

  • スライスと配列の要素型が一致し、配列がスライスより長くない場合、スライスを配列に変換できます。
  • 組み込みのパッケージunsafeには、新しい関数SliceDataString、およびStringDataが含まれています。
  • 比較可能型(通常のインターフェースなど)は、型引数が厳密に比較可能でなくても、comparable制約を満たすことができます。

Go 1.21

  • 事前宣言された関数のセットには、新しい関数minmax、およびclearが含まれています。
  • 型推論は、推論のためにインターフェースメソッドの型を使用します。また、変数に代入されたり、他の(おそらくジェネリックな)関数に引数として渡されたりするジェネリック関数に対して型引数を推論します。

Go 1.22

  • "for" ステートメントでは、各イテレーションで同じ変数を共有するのではなく、独自のイテレーション変数セットを持ちます。
  • "range" 句を持つ "for" ステートメントは、ゼロから上限までの整数値を繰り返し処理することができます。

Go 1.23

  • "range" 句を持つ "for" ステートメントは、レンジ式としてイテレータ関数を受け入れます。

Go 1.24

型統一ルール

型統一ルールは、2つの型が統一されるかどうか、およびどのように統一されるかを記述します。正確な詳細はGo実装に関係し、エラーメッセージの具体的な内容(コンパイラが型推論エラーを報告するかどうかなど)に影響を与え、まれなコード状況で型推論が失敗する理由を説明する場合があります。しかし、概してこれらのルールはGoコードを書く際には無視できます。型推論はほとんど「期待通りに機能する」ように設計されており、統一ルールもそれに合わせて微調整されています。

型統一はマッチングモードによって制御され、厳密または緩やかのいずれかです。統一が複合型構造を再帰的に下降する際、型の要素に使用されるマッチングモードである要素マッチングモードは、2つの型が代入可能性A)について統一される場合を除き、マッチングモードと同じままです。この場合、マッチングモードはトップレベルでは緩やかですが、その後要素型については厳密に変化し、型が代入可能であるために同一である必要がないという事実を反映します。

結合されていない型パラメータではない2つの型は、以下の条件のいずれかが真である場合に厳密に統一されます。

  • 両方の型が同一である。
  • 両方の型が同一の構造を持ち、その要素型が厳密に統一される場合。
  • ちょうど1つの型が非バインド型パラメータであり、その型セット内のすべての型が、Aの統一ルールに従って他の型と統一される場合(トップレベルでの緩やかな統一と、要素型に対する厳密な統一)。

両方の型が結合型パラメータである場合、以下の条件が満たされると、与えられたマッチングモードに従って統一されます。

  • 両方の型パラメータが同一である。
  • 型パラメータのどちらか一方が既知の型引数を持っている。この場合、型パラメータは結合されます。両方とも同じ型引数を表します。どちらの型パラメータもまだ既知の型引数を持っていない場合、一方の型パラメータに推論された将来の型引数は、両方の型パラメータに対して同時に推論されます。
  • 両方の型パラメータが既知の型引数を持ち、型引数が指定されたマッチングモードに従って統一される場合。

単一の結合型パラメータPと別の型Tは、以下の条件が満たされる場合に、指定されたマッチングモードに従って統一されます。

  • Pは既知の型引数を持っていない。この場合、TPの型引数として推論される。
  • Pは既知の型引数Aを持ち、ATは指定されたマッチングモードに従って統一され、以下の条件のいずれかが真である場合。
    • ATの両方がインターフェース型の場合: この場合、ATの両方が定義された型である場合、それらは同一でなければなりません。そうでなければ、どちらも定義された型でない場合、それらは同じ数のメソッドを持っていなければなりません(ATの統一は、メソッドが一致することを既に確立しています)。
    • ATもインターフェース型ではない場合:この場合、Tが定義された型であれば、TAに代わってPの推論された型引数となります。

最後に、結合型パラメータではない2つの型は、以下の条件が満たされる場合に緩やかに統一されます(要素マッチングモードに従って)。

  • 両方の型が厳密に統一される。
  • 一方の型が定義型であり、もう一方の型が型リテラル(ただしインターフェースではない)であり、その基底型が要素マッチングモードに従って統一される場合。
  • 両方の型は同一の型項を持つインターフェース(ただし型パラメータではない)であり、両方またはどちらも事前宣言された型comparableを埋め込み、対応するメソッドの型は完全に統一され、いずれかのインターフェースのメソッドセットがもう一方のインターフェースのメソッドセットのサブセットである場合。
  • 一方の型のみがインターフェース(ただし型パラメータではない)であり、2つの型の対応するメソッドは要素マッチングモードに従って統一され、そのインターフェースのメソッドセットがもう一方の型のメソッドセットのサブセットである場合。
  • 両方の型が同じ構造を持ち、その要素型が要素マッチングモードに従って統一される場合。