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

言語バージョン go1.23 (2024年6月13日)

はじめに

これはGoプログラミング言語の参照マニュアルです。ジェネリクスを含まないGo1.18以前のバージョンはこちらにあります。詳細情報やその他のドキュメントについては、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標準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. 一般コメントは、文字シーケンス/*で始まり、次の文字シーケンス*/で終わります。

コメントは、runeまたは文字列リテラル内、またはコメント内では開始できません。改行を含まない一般コメントは空白として機能します。その他のコメントは改行として機能します。

トークン

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

セミコロン

正式な構文では、セミコロン";"を多くの生成規則の終端子として使用します。Goプログラムでは、次の2つの規則を使用して、これらのセミコロンの大部分を省略できます。

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

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

識別子

識別子は、変数や型などのプログラムエンティティに名前を付けます。識別子は、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進数以外の基数を設定します。0bまたは0Bは2進数、00o、または0Oは8進数、0xまたは0Xは16進数[Go 1.13]。単一の0は10進数の0と見なされます。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進数字)、および指数部(eまたはEの後にオプションの符号と10進数字が続く)で構成されます。整数部または小数部のいずれかを省略できます。小数点または指数部のいずれかを省略できます。指数値expは、仮数(整数部と小数部)を10exp倍します。

16進浮動小数点リテラルは、0xまたは0Xプレフィックス、整数部(16進数字)、基数点、小数部(16進数字)、および指数部(pまたはPの後にオプションの符号と10進数字が続く)で構成されます。整数部または小数部のいずれかを省略できます。基数点を省略することもできますが、指数部は必須です。(この構文は、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

runeリテラル

ルーンリテラルは、Unicodeコードポイントを識別する整数値であるルーン定数を表します。ルーンリテラルは、シングルクォートで囲まれた1文字以上の文字列として表されます(例:'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を超える値やサロゲートペアの半分などは、無効な値があります。

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

\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いくつかの式に適用されたcapまたはlen、複素数定数に適用されたrealimag、および数値定数に適用されたcomplexによって表されます。ブール値は、あらかじめ宣言された定数truefalseによって表されます。あらかじめ宣言された識別子iotaは、整数定数を示します。

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

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

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

定数には、定数宣言または変換によって明示的に型を指定するか、変数宣言代入文、またはのオペランドとして使用するときに暗黙的に型を指定できます。定数値をそれぞれの型の値として表現できない場合はエラーになります。型が型パラメーターの場合、定数は型パラメーターの非定数値に変換されます。

型なし定数には、デフォルト型があります。これは、型付き値が必要なコンテキスト(たとえば、i := 0などの短い変数宣言で明示的な型がない場合)で定数が暗黙的に変換される型です。型なし定数のデフォルト型は、ブール型、ルーン型、整数型、浮動小数点型、複素数型、文字列型のいずれかであり、ブール定数、ルーン定数、整数定数、浮動小数点定数、複素数定数、文字列定数であるかによって異なります。

実装上の制限:数値定数は言語では任意の精度を持ちますが、コンパイラは精度が限られた内部表現を使用して実装する場合があります。ただし、すべての実装では、

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

変数

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

変数宣言、または関数のパラメーターと結果については、関数宣言または関数リテラルのシグネチャによって、名前付き変数の記憶領域が予約されます。組み込み関数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型の値で表現可能な非負の定数でなければなりません。lenという組み込み関数を使用して、配列aの長さを調べることができます。要素は、整数インデックス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が与えられた場合、プロモーションされたメソッドは、次のように構造体のメソッドセットに含まれます。

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

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のいずれかを表します)を持つ場合、S1S2が持つ可能性のある他のメソッドや共有するメソッドに関係なく、FileインターフェースはS1S2の両方によって実装されます。

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

interface{}

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

同様に、型宣言内で、Lockerと呼ばれるインターフェースを定義するために表示されるこのインターフェース仕様を考えてみてください。

type Locker interface {
	Lock()
	Unlock()
}

S1S2

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

を実装する場合、FileインターフェースだけでなくLockerインターフェースも実装します。

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

やや一般的な形式では、インターフェースTは、インターフェース要素として(修飾されている可能性のある)インターフェース型名Eを使用できます。これは、TにインターフェースE埋め込むことと呼ばれます[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]。メソッドの仕様と合わせて、これらの要素により、インターフェースの型集合を次のように正確に定義できます。

「全ての非インターフェース型集合」という定量化は、対象のプログラムで宣言された全ての(非インターフェース)型だけでなく、あらゆるプログラムにおける全ての可能な型を指し、したがって無限です。同様に、特定のメソッドを実装する全ての非インターフェース型の集合が与えられた場合、それらの型のメソッド集合の積集合には、そのメソッドが正確に含まれます。これは、対象のプログラム内の全ての型が常にそのメソッドを別のメソッドとペアにしている場合でも当てはまります。

設計上、インターフェースの型集合には、インターフェース型は含まれません。

// 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
}

実装上の制限:ユニオン(2つ以上の項を持つ)には、あらかじめ宣言された識別子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です。

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で閉じることができます。受信演算子の多値代入形式は、受信した値がチャネルが閉じられる前に送信されたかどうかを報告します。

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

型と値のプロパティ

基礎となる型

各型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{}です。

コア型

非インターフェース型Tにはそれぞれコア型があり、これはT基礎となる型と同じです。

インターフェースTは、次の条件のいずれかが満たされている場合、コア型を持ちます。

  1. T型集合内の全ての型の基礎となる型である単一の型Uが存在する場合。
  2. Tの型集合が、同一の要素型Eを持つチャネル型のみを含み、方向性チャネルは全て同じ方向を持つ場合。

その他、コア型を持つインターフェースはありません。

インターフェースのコア型は、満たされている条件に応じて、次のいずれかになります。

  1. U
  2. Tが双方向チャネルのみを含む場合の型chan E、または存在する方向性チャネルの方向に応じて型chan<- Eまたは<-chan E

定義により、コア型は決して定義された型型パラメータ、またはインターフェース型ではありません。

コア型を持つインターフェースの例

type Celsius float32
type Kelvin  float32

interface{ int }                          // int
interface{ Celsius|Kelvin }               // float32
interface{ ~chan int }                    // chan int
interface{ ~chan int|~chan<- int }        // chan<- int
interface{ ~[]*data; String() string }    // []*data

コア型を持たないインターフェースの例

interface{}                               // no single underlying type
interface{ Celsius|float64 }              // no single underlying type
interface{ chan int | chan<- string }     // channels have different element types
interface{ <-chan int | chan<- int }      // directional channels have different directions

一部の操作(スライス式appendcopy)は、バイトスライスと文字列を受け入れる、少し緩い形式のコア型に依存します。具体的には、インターフェースTの型集合内の全ての型の基礎となる型が[]bytestringの正確に2つの型である場合、Tのコア型はbytestringと呼ばれます。

bytestringコア型を持つインターフェースの例

interface{ int }                          // int (same as ordinary core type)
interface{ []byte | string }              // bytestring
interface{ ~[]byte | myString }           // bytestring

bytestringは実際の型ではないことに注意してください。変数の宣言や他の型の構成には使用できません。これは、バイトシーケンス(バイトスライスまたは文字列である可能性があります)から読み取る一部の操作の動作を記述するためだけに存在します。

型同一性

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型パラメータではない場合、次の条件のいずれかが適用される場合、型Tの値で表現可能です。

  • xTによって決定される値の集合内にある場合。
  • T浮動小数点型であり、xをオーバーフローすることなくTの精度に丸めることができる場合。丸めはIEEE 754の丸め方向(最近接偶数への丸め)規則を使用しますが、IEEEの負のゼロはさらに単純化されて符号なしのゼロになります。定数値は、IEEEの負のゼロ、NaN、または無限大になることはありません。
  • Tは複合型であり、xコンポーネントreal(x)imag(x)Tのコンポーネント型(float32またはfloat64)の値で表現可能です。

Tが型パラメータの場合、xTの型集合内の各型で表現可能であれば、型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"文の各節は、暗黙的なブロックとして機能します。

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

宣言とスコープ

宣言は、ブランクでない識別子を、定数型パラメータ変数関数ラベル、またはパッケージにバインドします。プログラムのすべての識別子は宣言する必要があります。同じブロック内で識別子を2回宣言することはできず、ファイルブロックとパッケージブロックの両方で識別子を宣言することもできません。

ブランク識別子は、宣言内で他の識別子と同様に使用できますが、バインディングを導入しないため、宣言されません。パッケージブロックでは、識別子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 "=" Type .

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

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

型定義

型定義は、指定された型と同じ基になる型と操作を持つ新しい個別の型を作成し、識別子である型名をそれにバインドします。

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]

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

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

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

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

オペランド

オペランドは、式における基本的な値を表します。オペランドは、リテラル、(おそらく修飾された)非ブランクな識別子(定数変数、または関数を表す)、または括弧で囲まれた式です。

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のコア型Tは、構造体、配列、スライス、またはマップ型でなければなりません(型がTypeNameとして与えられている場合を除いて、構文によってこの制約が適用されます)。要素とキーの型は、型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を加えたものをインデックスとして使用します。最初の要素にキーがない場合、そのインデックスは0です。

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

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(または場合によっては*x。後述)のフィールドまたはメソッドfを表します。識別子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は、そのようなfが存在するTで最も浅い深さのフィールドまたはメソッドを表します。最も浅い深さで正確に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の場合、ff(t, 7)として呼び出され、t.f(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]

は、xによってインデックス付けされた配列、配列へのポインタ、スライス、文字列、またはマップaの要素を表します。値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つのバリエーションがあります。

単純なスライス式

一次式

a[low : high]

部分文字列またはスライスを作成します。aコア型は、文字列、配列、配列へのポインタ、スライス、またはbytestringでなければなりません。インデックス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インデックスが欠落している場合は0にデフォルト設定され、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[low : high : max]

単純なスライス式a[low : high]と同じ型、長さ、要素を持つスライスを作成します。さらに、結果のスライスの容量をmax - lowに設定することで制御します。最初のインデックスのみを省略できます。これは0にデフォルト設定されます。コア型aは、配列、配列へのポインタ、またはスライス(文字列ではない)でなければなりません。配列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がジェネリック関数の場合、呼び出したり関数値として使用したりする前に、インスタンス化する必要があります。

関数呼び出しでは、関数値と引数は通常の順序で評価されます。評価されると、呼び出しのパラメータは値渡しで関数に渡され、呼び出された関数が実行を開始します。関数の戻り値のパラメータは、関数が戻るときに値渡しで呼び出し元に渡されます。

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)

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

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

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内では、whoは最初の呼び出しではnilになり、2回目の呼び出しでは[]string{"Joe", "Anna", "Eileen"}になります。

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

スライスsと呼び出しを考えると

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

Greeting内では、whoは、同じ基底配列を持つsと同じ値になります。

インスタンス化

ジェネリック関数または型は、型パラメータに型引数を代入することでインスタンス化されます [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`に代入可能でなければなりません。複雑さを軽減するために、型推論は代入の方向性を無視するため、`Slice`と`S`間の型関係は、(対称的な)型方程式`Slice ≡A S`(または`S ≡A Slice`)で表すことができます。ここで、`≡A`の`A`は、左辺と右辺の型が代入可能性規則に従って一致する必要があることを示しています(詳細は型統一のセクションを参照)。同様に、型パラメータ`S`は、その制約`~[]E`を満たす必要があります。これは、`S ≡C ~[]E`として表すことができます。ここで、`X ≡C Y`は「`X`は制約`Y`を満たす」ことを意味します。これらの観察から、2つの式からなる集合が得られます。

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

これは、型パラメータ`S`と`E`について解くことができます。(1)から、コンパイラは`S`の型引数が`Slice`であると推論できます。同様に、`Slice`の基礎となる型は`[]int`であり、`[]int`は制約の`[]E`と一致する必要があるため、コンパイラは`E`が`int`であると推論できます。したがって、この2つの式について、型推論は以下を推論します。

	S ➞ Slice
	E ➞ int

型方程式の集合が与えられると、解くべき型パラメータは、インスタンス化する必要があり、明示的な型引数が提供されていない関数の型パラメータです。これらの型パラメータは、*束縛* 型パラメータと呼ばれます。例えば、上記の`dedup`の例では、型パラメータ`S`と`E`は`dedup`に束縛されています。汎用関数呼び出しへの引数は、それ自体が汎用関数である可能性があります。その関数の型パラメータは、束縛型パラメータの集合に含まれます。関数引数の型には、他の関数(関数呼び出しを囲む汎用関数など)の型パラメータが含まれる場合があります。これらの型パラメータも型方程式に現れる可能性がありますが、そのコンテキストでは束縛されていません。型方程式は常に束縛型パラメータについてのみ解かれます。

型推論は、汎用関数の呼び出しと、汎用関数を(明示的に関数型である)変数への代入をサポートします。これには、汎用関数を他の(おそらく汎用である)関数への引数として渡すこと、および汎用関数を結果として返すことが含まれます。型推論は、これらの各ケースに固有の一連の方程式で動作します。方程式は以下のとおりです(明確にするために型引数のリストは省略されています)。

  • `f`または関数引数`ai`が汎用関数である関数呼び出し`f(a0, a1, …)`の場合
    `ai`が型なし定数ではない対応する関数引数とパラメータの各ペア`(ai, pi)`は、方程式`typeof(pi) ≡A typeof(ai)`を生成します。
    `ai`が型なし定数`cj`であり、`typeof(pi)`が束縛型パラメータ`Pk`である場合、ペア`(cj, Pk)`は型方程式から個別に収集されます。

  • 汎用関数`f`を関数型の(非汎用)変数`v`に代入する代入`v = f`の場合
    typeof(v) ≡A typeof(f).

  • `f`が関数型の(非汎用)結果変数`r`への結果として返される汎用関数である戻り文`return …, f, …`の場合
    typeof(r) ≡A typeof(f).

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

型推論は、型なし定数を考慮する前に、型付きオペランドから得られた型情報に優先順位を付けます。したがって、推論は2つの段階で進みます。

  1. 型統一を使用して、束縛型パラメータについて型方程式が解かれます。統一に失敗した場合、型推論は失敗します。

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

これらの2つの段階の後でもすべての型引数が検出されない場合、型推論は失敗します。

2つの段階が成功した場合、型推論は各束縛型パラメータについて型引数を決定します。

	Pk ➞ Ak

型引数`Ak`は、他の束縛型パラメータ`Pk`を要素型として含む複合型である可能性があります(または単なる別の束縛型パラメータである可能性もあります)。繰り返しの簡素化のプロセスでは、各型引数内の束縛型パラメータは、それらの型パラメータに対するそれぞれの型引数で置き換えられ、各型引数が束縛型パラメータを含まなくなるまで繰り返されます。

型引数が、束縛型パラメータを通じて自分自身への循環参照を含む場合、簡素化、したがって型推論は失敗します。そうでない場合、型推論は成功します。

型統一

型推論は、*型統一* を通じて型方程式を解きます。型統一は、方程式の左辺と右辺の型を再帰的に比較します。ここで、どちらか一方または両方の型は、束縛型パラメータであるか、または束縛型パラメータを含んでいる可能性があり、左辺と右辺が一致する(同一になるか、コンテキストに応じて代入可能になる)ような、それらの型パラメータの型引数を検索します。そのために、型推論は、束縛型パラメータから推論された型引数へのマップを維持します。このマップは、型統一中に参照および更新されます。最初は、束縛型パラメータはわかっていますが、マップは空です。型統一中に、新しい型引数`A`が推論された場合、型パラメータから引数への対応するマッピング`P ➞ A`がマップに追加されます。逆に、型を比較する際に、既知の型引数(マップエントリが既に存在する型引数)が対応する型パラメータの代わりに使用されます。型推論が進むにつれて、すべての式が考慮されるまで、または統一が失敗するまで、マップはますます多くが設定されます。型統一のステップで失敗がなく、マップに各型パラメータのエントリがある場合、型推論は成功します。

例えば、束縛型パラメータ`P`を持つ型方程式が与えられた場合

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

型推論は空のマップから始まります。統一は最初に左辺と右辺の型の最上位構造を比較します。どちらも同じ長さの配列です。要素型が統一する場合に統一されます。両方の要素型は構造体です。それらが同じ数のフィールドを同じ名前で持ち、フィールド型が統一する場合に統一されます。`P`の型引数はまだ不明です(マップエントリはありません)。そのため、`P`と`string`を統一すると、マッピング`P ➞ string`がマップに追加されます。`list`フィールドの型を統一するには、`[]P`と`[]string`、したがって`P`と`string`を統一する必要があります。この時点で`P`の型引数は既知であるため(`P`のマップエントリがあります)、その型引数`string`が`P`の代わりに使用されます。そして、`string`は`string`と同一であるため、この統一ステップも成功します。方程式の左辺と右辺の統一はこれで完了です。型方程式は1つだけであり、統一ステップで失敗がなく、マップが完全に設定されているため、型推論は成功します。

統一は、2つの型が同一でなければならないか、代入可能でなければならないか、または構造的に等しいだけでなければならないかによって、*正確な* 統一と*緩やかな* 統一の組み合わせを使用します。それぞれの型統一規則は、付録で詳細に説明されています。

代入(パラメータの受け渡しと戻り文を含む)に関与する型である`X`と`Y`が`X ≡A Y`形式の方程式の場合、最上位の型構造は緩やかに統一される可能性がありますが、要素型は正確に統一され、代入の規則と一致する必要があります。

`P`が型パラメータであり、`C`が対応する制約である`P ≡C C`形式の方程式の場合、統一規則は少し複雑です。

  • `C`にコア型`core(C)`があり、`P`に既知の型引数`A`がある場合、`core(C)`と`A`は緩やかに統一されなければなりません。`P`に既知の型引数がなく、`C`に基礎となる(チルダ)型ではない型項`T`が正確に1つ含まれている場合、統一はマッピング`P ➞ T`をマップに追加します。
  • `C`にコア型がなく、`P`に既知の型引数`A`がある場合、`A`は`C`のすべてのメソッド(もしあれば)を持ち、対応するメソッド型は正確に統一されなければなりません。

型制約からの型方程式を解く場合、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   = "+" | "-" | "!" | "^" | "*" | "&" | "<-" .

比較については別の場所で説明されています。他の二項演算子の場合、シフトまたは型なし定数が関与しない限り、オペランド型は同一でなければなりません。定数のみを含む演算については、定数式のセクションを参照してください。

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

シフト式における右オペランドは、整数型[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型の最も小さい負の値である場合です。この場合、商q = x / -1xと等しくなり(そしてr = 0)、2の補数による整数オーバーフローが発生します。

                         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の場合、左オペランドが1でn回シフトされたかのように動作します。その結果、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の場合、アドレス演算&xは、xへの型*Tのポインタを生成します。オペランドはアドレス指定可能でなければなりません。つまり、変数、ポインタ間接参照、またはスライスインデックス演算であるか、アドレス指定可能な構造体オペランドのフィールドセレクタであるか、アドレス指定可能な配列の配列インデックス演算である必要があります。アドレス指定可能性の要件の例外として、xは(括弧で囲まれた)複合リテラルでもかまいません。xの評価でランタイムパニックが発生する場合は、&xの評価でも発生します。

ポインタ型*Tのオペランドxの場合、ポインタ間接参照*xは、xによって指される型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.AppendRuneまたはutf8.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

定数式

定数式には、定数オペランドのみを含めることができ、コンパイル時に評価されます。

型なしのブール型、数値型、および文字列定数は、それぞれブール型、数値型、または文字列型のオペランドを使用できる場所でオペランドとして使用できます。

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

型なし定数に対するその他の演算では、同じ種類の型なし定数が生成されます。つまり、ブール型、整数型、浮動小数点型、複素数型、または文字列定数です。バイナリ演算(シフト以外)の型なしオペランドの種類が異なる場合、結果は、このリストで後に出現するオペランドの種類になります。整数、rune、浮動小数点、複素数。たとえば、型なし整数定数を型なし複素数定数で除算すると、型なし複素数定数が生成されます。

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を型なし整数、rune、または浮動小数点定数に適用すると、型なし複素数定数が生成されます。

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

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

評価順序

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

たとえば、(関数ローカルの)代入では

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

関数呼び出しと通信は、f()h()zがfalseと評価される場合)、i()j()<-cg()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"文が範囲句を使用していない場合。
  6. "switch"文において、
    • "switch"文を参照する"break"文がなく、
    • デフォルトケースが存在し、
    • 各ケース(デフォルトを含む)の文リストが終端文、または場合によってはラベル付きの"fallthrough"文で終わる場合。
  7. "select"文において、
    • "select"文を参照する"break"文がなく、
    • 各ケース(存在する場合のデフォルトを含む)の文リストが終端文で終わる場合。
  8. ラベル付き文で終端文をラベル付けしている場合。

その他のすべての文は終端文ではありません。

文リストは、リストが空でない場合、かつ最後の空でない文が終端文である場合、終端文で終わります。

空文

空文は何もしません。

EmptyStmt = .

ラベル付き文

ラベル付き文は、gotobreakcontinue文のターゲットになる可能性があります。

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

IncDec文

"++"と"--"文は、そのオペランドを型なし定数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は2項算術演算子)はx = x op (y)と等価ですが、xを1回だけ評価します。op=構成要素は単一のトークンです。代入演算では、左辺と右辺の両方の式リストは正確に1つの単一値の式を含んでいなければならず、左辺の式はブランク識別子であってはなりません。

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

タプル代入は、多値演算の個々の要素を変数のリストに代入します。2つの形式があります。最初の形式では、右辺オペランドは関数呼び出し、チャネルまたはマップ操作、または型アサーションなどの単一の多値式です。左辺のオペランドの数は、値の数と一致する必要があります。たとえば、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型に暗黙的に変換されます。

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つの形式があります。式スイッチと型スイッチです。式スイッチでは、ケースにはスイッチ式の値と比較される式が含まれています。型スイッチでは、ケースには、特別に注釈付けされたスイッチ式の種類と比較される型が含まれています。スイッチ式は、スイッチ文で正確に1回評価されます。

式スイッチ

式スイッチでは、スイッチ式が評価され、ケース式(定数である必要はありません)が左から右、上から下に評価されます。スイッチ式と等しい最初の式が、関連付けられたケースの文の実行をトリガーします。他のケースはスキップされます。一致するケースがなく、"default"ケースがある場合は、その文が実行されます。"default"ケースは最大1つ存在し、"switch"文のどこにでも配置できます。スイッチ式がない場合は、ブール値trueと同等です。

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

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

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

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

ケースまたはデフォルト句では、最後の空でない文は(場合によってはラベル付きの)"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インターフェース型でなければなりませんが、型パラメータではなく、ケースにリストされている各非インターフェース型Txの型を実装する必要があります。型スイッチのケースにリストされている型はすべて異なる必要があります。

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文

For句を持つ"for"文は、その条件によって制御されますが、さらにinit文とpost文(代入、増分または減分文など)を指定できます。init文は短い変数宣言にすることができますが、post文はできません。

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

init文が空でない場合、最初の反復の条件を評価する前に一度実行されます。post文はブロックの実行ごとに(そしてブロックが実行された場合のみ)実行されます。For句の要素は空でも構いませんが、セミコロンは、条件のみがある場合を除いて必須です。条件がない場合、論理値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"文は、配列、スライス、文字列、マップのすべてのエントリ、チャネルで受信された値、0から上限までの整数値[Go 1.22]、またはイテレータ関数のyield関数に渡された値[Go 1.23]を反復処理します。各エントリについて、存在する場合、対応する反復変数反復値を代入し、その後ブロックを実行します。

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

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

range式xはループを開始する前に評価されますが、例外があります。反復変数が1つ以下で、xまたはlen(x)定数の場合、range式は評価されません。

左側の関数呼び出しは、反復ごとに1回評価されます。各反復について、対応する反復変数が存在する場合、反復値は次のように生成されます。

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つ以下の場合、rangeループは0からlen(a)-1までの反復値を生成し、配列またはスライス自体にはインデックス付けしません。nilスライスの場合、反復回数は0です。
  2. 文字列値の場合、"range"句は、バイトインデックス0から始まる文字列内のUnicodeコードポイントを反復処理します。連続する反復では、インデックス値は文字列内の連続するUTF-8でエンコードされたコードポイントの最初のバイトのインデックスになり、2番目の値(rune型)は対応するコードポイントの値になります。反復で無効なUTF-8シーケンスが検出されると、2番目の値は0xFFFD(Unicode置換文字)になり、次の反復では文字列内の1バイトが前進します。
  3. マップの反復順序は指定されておらず、反復ごとに同じであるとは限りません。まだ到達していないマップエントリが反復中に削除された場合、対応する反復値は生成されません。反復中にマップエントリが作成された場合、そのエントリは反復中に生成されるか、スキップされる可能性があります。その選択は、作成された各エントリごとに、そして反復ごとに異なる場合があります。マップがnilの場合、反復回数は0です。
  4. チャネルの場合、生成される反復値は、チャネルがクローズされるまでチャネルで送信された連続する値です。チャネルがnilの場合、range式は永久にブロックします。
  5. 整数値nn整数型または型指定されていない整数定数)の場合、0からn-1までの反復値が増加順で生成されます。nが整数型の場合、反復値は同じ型になります。それ以外の場合は、nの型は、反復変数に代入された場合と同じように決定されます。具体的には、反復変数が既存の場合、反復値の型は反復変数の型(整数型でなければなりません)になります。それ以外の場合は、反復変数が"range"句で宣言されているか、存在しない場合、反復値の型はnデフォルト型になります。n <= 0の場合、ループは反復を実行しません。
  6. 関数fの場合、反復は、新しい合成されたyield関数を引数としてfを呼び出すことによって進みます。fが戻る前にyieldが呼び出されると、yieldへの引数が、ループ本体を1回実行するための反復値になります。後続の各ループ反復の後、yieldはtrueを返し、ループを続けるために再び呼び出すことができます。ループ本体が終了しない限り(break文など)、"range"句はyield呼び出しごとにこの方法で反復値を生成し続けます。ループ本体が終了すると(break文など)、yieldはfalseを返し、再度呼び出すことはできません。

反復変数は、短い変数宣言:=)の形式を使用して"range"句で宣言できます。この場合、それらのスコープは"for"文のブロックであり、各反復は独自の新しい変数を持っています[Go 1.22](For句を持つ"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"文に入る際に、ソースの順序で正確に1回評価されます。その結果、受信または送信するチャネルのセットと、送信する対応する値が得られます。その評価における副作用は、どの(もしあれば)通信操作が選択されて実行されるかに関係なく発生します。短い変数宣言または代入を持つ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:

は、vの作成をスキップするため、誤りです。

ブロックの外側の「goto」文は、そのブロック内のラベルにジャンプできません。例えば、この例

if n%2 == 1 {
	goto L1
}
for n > 0 {
	f()
	n--
L1:
	f()
	n--
}

は、「for」文のブロック内にラベルL1があるのに対し、gotoがないため、誤りです。

fallthrough文

式「switch」文では、「fallthrough」文は制御を次のcase節の最初の文に転送します。これは、そのような節の最後の空でない文としてのみ使用できます。

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は、ゼロ個以上の値xをスライスsに追加し、sと同じ型である結果のスライスを返します。sのコア型は[]E型のスライスでなければなりません。値x...E型の仮引数に渡され、それぞれの仮引数渡し規則が適用されます。特別なケースとして、sのコア型が[]byteの場合、appendはコア型bytestringの第2引数と...も受け入れます。この形式は、バイトスライスまたは文字列のバイトを追加します。

append(s S, x ...E) S  // core type of S is []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にスライス要素をコピーし、コピーされた要素数を返します。両方の引数のコア型は、同一の要素型を持つスライスでなければなりません。コピーされる要素数は、len(src)len(dst)の最小値です。特別なケースとして、宛先のコア型が[]byteの場合、copyはコア型bytestringのソース引数も受け入れます。この形式は、バイトスライスまたは文字列からバイトスライスにバイトをコピーします。

copy(dst, src []T) int
copy(dst []byte, src string) int

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

組み込み関数clearは、mapslice、または型パラメータ型の引数を取り、すべての要素を削除またはゼロクリアします[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の引数の型が型パラメータの場合、その型セット内のすべての型はmapまたはsliceでなければならず、clearは実際の型引数に対応する操作を実行します。

mapまたはsliceがnilの場合、clearはノーオペレーションです。

close

コア型がチャネルである引数chの場合、組み込み関数closeは、チャネルに値が送信されなくなることを記録します。chが受信専用チャネルの場合はエラーです。閉じられたチャネルへの送信または閉じられたチャネルのクローズは、実行時パニックを引き起こします。nilチャネルのクローズも実行時パニックを引き起こします。closeを呼び出した後、および以前に送信されたすべての値が受信された後、受信操作はブロックせずにチャネルの型のゼロ値を返します。複数値の受信操作は、受信した値とチャネルが閉じているかどうかを示す値を返します。

複素数の操作

3つの関数が、複素数を組み立てて分解します。組み込み関数complexは、浮動小数点の実数部と虚数部から複素数値を構築し、realimagは複素数の値の実数部と虚数部を抽出します。

complex(realPart, imaginaryPart floatT) complexT
real(complexT) floatT
imag(complexT) floatT

引数と戻り値の型は対応しています。complexの場合、2つの引数は同じ浮動小数点型でなければならず、戻り値の型は対応する浮動小数点構成要素を持つ複素数型です。float32引数の場合complex64float64引数の場合complex128です。引数のいずれかが型なし定数に評価された場合、最初に他の引数の型に暗黙的に変換されます。両方の引数が型なし定数に評価される場合、それらは複素数でないか、虚数部がゼロでなければならず、関数の戻り値は型なし複素数定数です。

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

型パラメータ型の引数は許可されません。

map要素の削除

組み込み関数deleteは、mapmからキーkを持つ要素を削除します。値kmのキー型に代入可能でなければなりません。

delete(m, k)  // remove element m[k] from map m

mの型が型パラメータの場合、その型セット内のすべての型はmapでなければならず、すべて同一のキー型を持たなければなりません。

mapmnilであるか、要素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スライス、map、チャネルの長さは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*Tではありません)の値を返します。初期値に関するセクションで説明されているように、メモリは初期化されます。

Call             Core type    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

サイズ引数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

マップ型とサイズヒントnを使用してmakeを呼び出すと、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の呼び出しに渡された値になります。新しいpanicを開始せずにDが正常に返ると、パニックシーケンスは停止します。その場合、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の解釈は実装依存ですが、通常はコンパイルされたパッケージの完全なファイル名の部分文字列であり、インストールされたパッケージのリポジトリを基準にする場合があります。

実装上の制約:コンパイラは、UnicodeのL、M、N、P、Sの一般カテゴリ(スペースのないグラフィック文字)に属する文字のみを使用する非空の文字列にImportPathを制限し、文字!"#$%&'()*,:;<=>?[\]^`{|}と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つのパッケージをインポートする場合、インポートされたパッケージは1回だけ初期化されます。パッケージのインポートは、構造上、循環的な初期化依存関係が発生しないことを保証します。より正確には

インポートパスでソートされたすべてのパッケージのリストが与えられた場合、各ステップで、インポートされたすべてのパッケージ(もしあれば)が既に初期化されているリスト内の最初の未初期化パッケージが初期化されます。このステップは、すべてのパッケージが初期化されるまで繰り返されます。

パッケージの初期化(変数の初期化と`init`関数の呼び出し)は、一度に1つのパッケージずつ、シーケンシャルに、単一のゴルーチンで発生します。`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)

実行時パニック

配列の範囲外のインデックス付けを試みるなど、実行時エラーは、実装定義のインターフェース型`runtime.Error`の値を使用して組み込み関数`panic`を呼び出すことと同等の実行時パニックを引き起こします。その型は、事前に宣言されたインターフェース型`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`の型に変換でき、その逆も可能です。`Pointer`と`uintptr`の間の変換の効果は実装定義です。

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

関数`Alignof`と`Sizeof`は、任意の型の式`x`を受け取り、仮の変数`v`が`var v = x`によって宣言された場合のように、その仮の変数の配置またはサイズをそれぞれ返します。

関数`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`の(変数は)`T`が型パラメータである場合、または可変サイズの要素またはフィールドを含む配列または構造体型である場合、可変サイズを持ちます。それ以外の場合は、サイズは定数です。`Alignof`、`Offsetof`、`Sizeof`への呼び出しは、引数(または`Offsetof`のセレクタ式`s.f`の構造体`s`)が定数サイズの型である場合、`uintptr`型のコンパイル時定数式です。

関数`Add`は`len`を`ptr`に追加し、更新されたポインタ`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))[:]

ただし、特別なケースとして、`ptr`が`nil`で`len`がゼロの場合、`Slice`は`nil`を返します [Go 1.17]。

`len`引数は整数型または型なしの定数でなければなりません。定数の`len`引数は、非負で、`int`型の値で表現可能でなければなりません。型なし定数の場合、`int`型が与えられます。実行時において、`len`が負の場合、または`ptr`が`nil`で`len`がゼロでない場合、実行時パニックが発生します [Go 1.17]。

関数`SliceData`は、`slice`引数の基になる配列へのポインタを返します。スライスの容量`cap(slice)`がゼロでない場合、そのポインタは`&slice[:1][0]`です。`slice`が`nil`の場合、結果は`nil`です。それ以外の場合は、未指定のメモリアドレスへの`nil`でないポインタです [Go 1.20]。

関数`String`は、基になるバイトが`ptr`で始まり、長さが`len`である`string`値を返します。`ptr`と`len`引数には、関数`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プログラムは、それ以降の任意のバージョンでも動作し続けることを保証します。

たとえば、2進整数リテラルにプレフィックス`0b`を使用する機能は、Go 1.13で導入されました。これは整数リテラルに関するセクションの[Go 1.13]で示されています。`0b1011`などの整数リテラルを含むソースコードは、コンパイラによって使用される暗黙的または明示的な言語バージョンがGo 1.13より古い場合、拒否されます。

次の表は、Go 1以降に導入された機能に必要な最小言語バージョンを示しています。

Go 1.9

Go 1.13

  • 整数リテラルは、プレフィックス`0b`、`0B`、`0o`、`0O`をそれぞれ2進数および8進数のリテラルに使用できます。
  • 16進数の浮動小数点リテラルは、接頭辞0x0Xを使用して記述できます。
  • 虚数サフィックスiは、(2進数、10進数、16進数の)整数または浮動小数点リテラルのいずれにも使用できます。10進数リテラルのみに限定されません。
  • 数値リテラルの桁は、アンダースコア_を使用して区切る(グループ化)ことができます。
  • シフト演算におけるシフトカウントは、符号付き整数型にすることができます。

Go 1.14

Go 1.17

  • スライスは、スライスと配列の要素型が一致し、配列がスライスよりも長くない場合、配列ポインタに変換できます。
  • 組み込みパッケージunsafeには、新しい関数AddSliceが含まれています。

Go 1.18

1.18リリースでは、多相関数と型(「ジェネリクス」)が言語に追加されました。具体的には

  • 演算子と句読点の集合には、新しいトークン~が含まれています。
  • 関数と型の宣言では、型パラメータを宣言できます。
  • インターフェース型は、任意の型(インターフェースの型名だけでなく)と、union型および~T型要素を埋め込むことができます。
  • プリデクラード型の集合には、新しい型anycomparableが含まれています。

Go 1.20

  • スライスは、スライスと配列の要素型が一致し、配列がスライスよりも長くない場合、配列に変換できます。
  • 組み込みパッケージunsafeには、新しい関数SliceDataStringStringDataが含まれています。
  • 比較可能な型(通常のインターフェースなど)は、型引数が厳密に比較可能でない場合でも、comparable制約を満たすことができます。

Go 1.21

  • プリデクラード関数の集合には、新しい関数minmaxclearが含まれています。
  • 型推論は、インターフェースメソッドの型を推論に使用します。また、変数に代入されたり、他の(おそらくジェネリックな)関数に引数として渡されたりするジェネリック関数の型引数も推論します。

Go 1.22

  • "for"文では、各反復は独自の反復変数のセットを持ち、各反復で同じ変数を共有することはありません。
  • "range"句付きの"for"文は、0から上限までの整数値を反復処理できます。

Go 1.23

  • "range"句付きの"for"文は、イテレータ関数を範囲式として受け入れます。

型統一規則

型統一規則は、2つの型がどのように統一されるかを記述します。正確な詳細はGoの実装に関連し、エラーメッセージの具体的(コンパイラが型推論エラーまたはその他のエラーを報告するかどうかなど)に影響を与え、珍しいコード状況で型推論が失敗する理由を説明することがあります。しかし、概してこれらの規則はGoコードを作成する際に無視できます。型推論は主に「期待通りに動作する」ように設計されており、統一規則はそれに応じて微調整されています。

型統一は、一致モードによって制御されます。一致モードは、正確または緩やかのいずれかです。統一が複合型構造を再帰的に降下する際に、型の要素に使用される一致モード(要素一致モード)は、2つの型が代入可能性A)のために統一される場合を除いて、一致モードと同じままです。この場合、最上位レベルでは一致モードは緩やかですが、要素型では正確に変化します。これは、型が代入可能であるためには同一である必要がないという事実を反映しています。

束縛型パラメータではない2つの型は、次の条件のいずれかが真の場合に正確に統一されます。

  • 両方の型が同一です。
  • 両方の型が同一の構造を持ち、その要素型が正確に統一されます。
  • 束縛されていない型パラメータが正確に1つあり、コア型を持ち、そのコア型がA(最上位レベルでの緩やかな統一と要素型に対する正確な統一)の統一規則に従って他の型と統一されます。

両方の型が束縛型パラメータである場合、指定された一致モードに従って統一されます。

  • 両方の型パラメータが同一です。
  • 型パラメータの多くても1つに既知の型引数があります。この場合、型パラメータは結合されます。つまり、両方とも同じ型引数を表します。いずれの型パラメータにもまだ既知の型引数がない場合、一方の型パラメータに対して推論される将来の型引数は、同時に両方の型パラメータに対して推論されます。
  • 両方の型パラメータに既知の型引数があり、型引数が指定された一致モードに従って統一されます。

単一の束縛型パラメータPと別の型Tは、指定された一致モードに従って統一されます。

  • Pに既知の型引数がありません。この場合、TPの型引数として推論されます。
  • Pに既知の型引数Aがあり、ATが指定された一致モードに従って統一され、次の条件のいずれかが真です。
    • ATの両方がインターフェース型です。この場合、ATの両方が定義済み型でもある場合、同一である必要があります。そうでない場合、それらのいずれも定義済み型でない場合、同じ数のメソッドを持っている必要があります(ATの統一によってメソッドが一致することが既に確立されています)。
    • ATのどちらもインターフェース型ではありません。この場合、Tが定義済み型である場合、TPの推論された型引数としてAに置き換えられます。

最後に、束縛型パラメータではない2つの型は、(要素一致モードに従って)緩やかに統一されます。

  • 両方の型が正確に統一されます。
  • 一方の型は定義済み型であり、もう一方の型は型リテラルですがインターフェースではなく、その基になる型は要素一致モードに従って統一されます。
  • 両方の型がインターフェース(型パラメータではありません)であり、型項が同一であり、プリデクラード型comparableを両方とも埋め込むか、どちらも埋め込まないか、対応するメソッド型が正確に統一され、一方のインターフェースのメソッドセットがもう一方のインターフェースのメソッドセットのサブセットです。
  • 一方の型のみがインターフェース(型パラメータではありません)であり、2つの型の対応するメソッドが要素一致モードに従って統一され、インターフェースのメソッドセットがもう一方の型のメソッドセットのサブセットです。
  • 両方の型が同じ構造を持ち、その要素型が要素一致モードに従って統一されます。