Go 言語仕様
Table of Contents
ライセンス
- 本ページは The Go Programming Language Specification (© 2009-2025 The Go Authors) を翻訳したものです。
- 原文テキストは CC BY 4.0 の下で公開されています。
- HTML を分割 Markdown 化し Claude 3.7 sonnet で翻訳しています。
- 仕様書中のコード片は BSD 3-Clause License に従います。
- 本訳は非公式であり、The Go Authors が本訳を承認・保証するものではありません。
序論 #
これはGoプログラミング言語のリファレンスマニュアルです。 さらなる情報や他のドキュメントについては、go.devをご覧ください。
Goはシステムプログラミングを念頭に設計された汎用言語です。強い型付けとガベージコレクションを備え、並行プログラミングを明示的にサポートしています。プログラムはパッケージから構築され、その特性によって依存関係の効率的な管理が可能になります。
構文はコンパクトで解析しやすく、統合開発環境などの自動ツールによる簡単な分析が可能です。
表記法 #
構文は拡張バッカス・ナウア記法(EBNF)の変種を使用して指定されています:
Syntax = { Production } .
Production = production_name "=" [ Expression ] "." .
Expression = Term { "|" Term } .
Term = Factor { Factor } .
Factor = production_name | token [ "…" token ] | Group | Option | Repetition .
Group = "(" Expression ")" .
Option = "[" Expression "]" .
Repetition = "{" Expression "}" .
プロダクションは項から構築された式であり、以下の演算子を優先度の低い順に使用します:
| 選択肢
() グループ化
[] オプション(0または1回)
{} 繰り返し(0からn回)
小文字のプロダクション名は字句(終端)トークンを識別するために使用されます。非終端記号は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 = /* Unicodeコードポイント U+000A */ .
unicode_char = /* 改行を除く任意のUnicodeコードポイント */ .
unicode_letter = /* "Letter"カテゴリに分類されるUnicodeコードポイント */ .
unicode_digit = /* "Number, decimal digit"カテゴリに分類されるUnicodeコードポイント */ .
Unicode標準8.0のセクション4.5「General Category」では、文字カテゴリのセットが定義されています。GoはLetterカテゴリ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つの形式があります:
- 行コメントは文字シーケンス
//
で始まり、行の終わりで終了します。 - 一般コメントは文字シーケンス
/*
で始まり、最初の後続文字シーケンス*/
で終了します。
コメントはルーンまたは文字列リテラルの内部、またはコメントの内部から始めることはできません。改行を含まない一般コメントはスペースのように動作します。その他のコメントは改行のように動作します。
トークン #
トークンはGo言語の語彙を形成します。4つのクラスがあります:識別子、キーワード、演算子と句読点、およびリテラル。スペース(U+0020)、水平タブ(U+0009)、キャリッジリターン(U+000D)、改行(U+000A)から形成される空白は、それらが結合して単一のトークンになるようなトークンを分離する場合を除いて無視されます。また、改行またはファイルの終わりはセミコロンの挿入をトリガーすることがあります。入力をトークンに分割する際、次のトークンは有効なトークンを形成する最長の文字シーケンスです。
セミコロン #
形式構文では、いくつかのプロダクションでセミコロン ;
を終端として使用しています。Goプログラムでは、以下の2つのルールを使用して、これらのセミコロンのほとんどを省略することができます:
入力がトークンに分割されるとき、そのトークンが以下のいずれかである場合、行の最後のトークンの直後にセミコロンがトークンストリームに自動的に挿入されます:
複雑な文が単一の行を占めることを可能にするために、閉じる
)
または}
の前のセミコロンは省略することができます。
慣用的な使用法を反映するために、このドキュメントのコード例ではこれらのルールを使用してセミコロンを省略しています。
識別子 #
識別子は変数や型などのプログラムエンティティに名前を付けます。識別子は1つ以上の文字と数字のシーケンスです。識別子の最初の文字は文字でなければなりません。
identifier = letter { letter | unicode_digit } .
a
_x9
ThisVariableIsExported
αβ
一部の識別子は事前宣言されています。
キーワード #
以下のキーワードは予約されており、識別子として使用することはできません。
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
演算子と句読点 #
以下の文字シーケンスは演算子(代入演算子を含む)と句読点を表します [Go 1.18]:
+ & += &= && == != ( )
- | -= |= || < <= [ ]
* ^ *= ^= <- > >= { }
/ << /= <<= ++ = := , ;
% >> %= >>= -- ! ... . :
&^ &^= ~
整数リテラル #
整数リテラルは整数定数を表す数字のシーケンスです。オプションの接頭辞は10進数以外の基数を設定します:バイナリには0b
または0B
、8進数には0
、0o
、または0O
、16進数には0x
または0X
を使用します [Go 1.13]。単一の0
は10進数のゼロと見なされます。16進数リテラルでは、文字a
からf
およびA
からF
は値10から15を表します。
読みやすさのために、アンダースコア文字_
は基数接頭辞の後や連続する数字の間に表示することができます。このようなアンダースコアはリテラルの値を変更しません。
int_lit = decimal_lit | binary_lit | octal_lit | hex_lit .
decimal_lit = "0" | ( "1" … "9" ) [ [ "_" ] decimal_digits ] .
binary_lit = "0" ( "b" | "B" ) [ "_" ] binary_digits .
octal_lit = "0" [ "o" | "O" ] [ "_" ] octal_digits .
hex_lit = "0" ( "x" | "X" ) [ "_" ] hex_digits .
decimal_digits = decimal_digit { [ "_" ] decimal_digit } .
binary_digits = binary_digit { [ "_" ] binary_digit } .
octal_digits = octal_digit { [ "_" ] octal_digit } .
hex_digits = hex_digit { [ "_" ] hex_digit } .
42
4_2
0600
0_600
0o600
0O600 // 2番目の文字は大文字の「O」
0xBadFace
0xBad_Face
0x_67_7a_2f_cc_40_c6
170141183460469231731687303715884105727
170_141183_460469_231731_687303_715884_105727
_42 // 整数リテラルではなく識別子
42_ // 無効:_は連続する数字を分離する必要がある
4__2 // 無効:一度に_は1つだけ
0_xBadFace // 無効:_は連続する数字を分離する必要がある
浮動小数点リテラル #
浮動小数点リテラルは浮動小数点定数の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 (整数の減算)
0x.p1 // 無効:仮数に数字がない
1p-2 // 無効:p指数は16進数の仮数が必要
0x1.5e-2 // 無効:16進数の仮数はp指数が必要
1_.5 // 無効:_は連続する数字を分離する必要がある
1._5 // 無効:_は連続する数字を分離する必要がある
1.5_e1 // 無効:_は連続する数字を分離する必要がある
1.5e_1 // 無効:_は連続する数字を分離する必要がある
1.5e1_ // 無効:_は連続する数字を分離する必要がある
虚数リテラル #
虚数リテラルは複素数定数の虚数部を表します。これは整数または浮動小数点リテラルの後に小文字i
が続くものです。虚数リテラルの値は、対応する整数または浮動小数点リテラルの値に虚数単位iを掛けた値です [Go 1.13]。
imaginary_lit = (decimal_digits | int_lit | float_lit) "i" .
後方互換性のために、完全に10進数(およびアンダースコア)のみで構成される虚数リテラルの整数部は、先頭に0
があっても10進整数と見なされます。
0i
0123i // 後方互換性のために == 123i
0o123i // == 0o123 * 1i == 83i
0xabci // == 0xabc * 1i == 2748i
0.i
2.71828i
1.e+0i
6.67428e-11i
1E6i
.25i
.12345E+5i
0x1p-2i // == 0x1p-2 * 1i == 0.25i
ルーンリテラル #
ルーンリテラルは、Unicodeコードポイントを識別する整数値であるルーン定数を表します。ルーンリテラルは、'x'
や'\n'
のように、一重引用符で囲まれた1つ以上の文字として表現されます。引用符の中には、改行とエスケープされていない一重引用符を除く任意の文字が表示できます。一重引用符で囲まれた単一の文字は文字自体のUnicode値を表し、バックスラッシュで始まる複数文字のシーケンスはさまざまな形式で値をエンコードします。
最も単純な形式は引用符内の単一の文字を表します。Goのソーステキストは UTF-8 でエンコードされた Unicode 文字であるため、複数の UTF-8 エンコードされたバイトが単一の整数値を表す場合があります。例えば、リテラル'a'
はリテラルa
(Unicode U+0061、値0x61
)を表す単一のバイトを保持しますが、'ä'
は2バイト(0xc3
0xa4
)を保持し、a-ダイエレシス(U+00E4、値0xe4
)を表します。
いくつかのバックスラッシュエスケープにより、任意の値をASCIIテキストとしてエンコードすることができます。数値定数として整数値を表す方法は4つあります:\x
の後に正確に2桁の16進数が続くもの、\u
の後に正確に4桁の16進数が続くもの、\U
の後に正確に8桁の16進数が続くもの、そして単純なバックスラッシュ\
の後に正確に3桁の8進数が続くものです。いずれの場合も、リテラルの値は対応する基数で表される数字の値です。
これらの表現はすべて整数になりますが、有効範囲が異なります。8進エスケープは0から255までの値を表す必要があります。16進エスケープはこの条件を構成によって満たします。エスケープ\u
と\U
はUnicodeコードポイントを表すため、その中のいくつかの値は不正です。特に0x10FFFF
を超える値とサロゲートハーフは不正です。
バックスラッシュの後、特定の単一文字エスケープは特別な値を表します:
\a U+0007 アラートまたはベル
\b U+0008 バックスペース
\f U+000C フォームフィード
\n U+000A ラインフィードまたは改行
\r U+000D キャリッジリターン
\t U+0009 水平タブ
\v U+000B 垂直タブ
\\ U+005C バックスラッシュ
\' U+0027 一重引用符(ルーンリテラル内のみで有効なエスケープ)
\" U+0022 二重引用符(文字列リテラル内のみで有効なエスケープ)
ルーンリテラル内のバックスラッシュの後に認識されない文字が続くことは不正です。
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'
'\'' // 一重引用符文字を含むルーンリテラル
'aa' // 不正:文字が多すぎる
'\k' // 不正:kはバックスラッシュの後に認識されない
'\xa' // 不正:16進数の桁が少なすぎる
'\0' // 不正:8進数の桁が少なすぎる
'\400' // 不正:8進数の値が255を超えている
'\uDFFF' // 不正:サロゲートハーフ
'\U00110000' // 不正:無効なUnicodeコードポイント
文字列リテラル #
文字列リテラルは、一連の文字を連結して得られる文字列定数を表します。生文字列リテラルと解釈付き文字列リテラルの2つの形式があります。
生文字列リテラルはバッククォート間の文字シーケンスで、foo
のようなものです。引用符の間には、バッククォートを除く任意の文字が表示できます。生文字列リテラルの値は、引用符の間の解釈されていない(暗黙的にUTF-8でエンコードされた)文字で構成される文字列です。特にバックスラッシュには特別な意味はなく、文字列には改行を含めることができます。生文字列リテラル内のキャリッジリターン文字(’\r’)は生の文字列値から破棄されます。
解釈付き文字列リテラルは、"bar"
のように、二重引用符間の文字シーケンスです。引用符の間には、改行とエスケープされていない二重引用符を除く任意の文字が表示できます。引用符の間のテキストはリテラルの値を形成し、バックスラッシュエスケープはルーンリテラルと同様に解釈されます(ただし、\'
は不正で\"
は合法です)。3桁の8進数(\
nnn)と2桁の16進数(\x
nn)エスケープは、結果の文字列の個々のバイトを表します。他のすべてのエスケープは個々の文字の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` // "abc"と同じ
`\n
\n` // "\\n\n\\n"と同じ
"\n"
"\"" // `"`と同じ
"Hello, world!\n"
"日本語"
"\u65e5本\U00008a9e"
"\xff\u00FF"
"\uD800" // 不正:サロゲートハーフ
"\U00110000" // 不正:無効なUnicodeコードポイント
これらの例はすべて同じ文字列を表します:
"日本語" // UTF-8入力テキスト
`日本語` // 生リテラルとしてのUTF-8入力テキスト
"\u65e5\u672c\u8a9e" // 明示的なUnicodeコードポイント
"\U000065e5\U0000672c\U00008a9e" // 明示的なUnicodeコードポイント
"\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e" // 明示的なUTF-8バイト
ソースコードがアクセントと文字を含む合成形式のように、文字を2つのコードポイントとして表す場合、ルーンリテラルに配置すると(単一のコードポイントではないため)エラーになり、文字列リテラルに配置すると2つのコードポイントとして表示されます。
定数 #
ブール定数、ルーン定数、整数定数、浮動小数点定数、複素数定数、および文字列定数があります。ルーン、整数、浮動小数点、および複素数定数は集合的に数値定数と呼ばれます。
定数値は、ルーン、整数、浮動小数点、虚数、または文字列リテラル、定数を表す識別子、定数式、定数結果を持つ変換、または定数引数に適用されたmin
やmax
などの一部の組み込み関数の結果値、特定の値に適用されたunsafe.Sizeof
、一部の式に適用されたcap
またはlen
、複素数定数に適用されたreal
とimag
、および数値定数に適用されたcomplex
によって表されます。ブール真理値は、事前宣言された定数true
とfalse
によって表されます。事前宣言された識別子iotaは整数定数を表します。
一般に、複素数定数は定数式の一形態であり、そのセクションで説明されています。
数値定数は任意精度の正確な値を表し、オーバーフローしません。したがって、IEEE 754の負のゼロ、無限大、および非数値(NaN)を表す定数はありません。
定数は型付きまたは型なしです。リテラル定数、true
、false
、iota
、および型なし定数オペランドのみを含む特定の定数式は型なしです。
定数には、定数宣言または変換によって明示的に型を与えるか、変数宣言や代入文で使用する場合、または式のオペランドとして使用する場合に暗黙的に型を与えることができます。定数値がそれぞれの型の値として表現できない場合はエラーです。型が型パラメータの場合、定数は型パラメータの非定数値に変換されます。
型なし定数にはデフォルト型があり、これは明示的な型が必要なコンテキストで定数が暗黙的に変換される型です。例えば、i := 0
のような明示的な型がない短い変数宣言などです。型なし定数のデフォルト型は、ブール、ルーン、整数、浮動小数点、複素数、または文字列定数かどうかに応じて、それぞれbool
、rune
、int
、float64
、complex128
、またはstring
です。
実装上の制限:言語では数値定数は任意の精度を持ちますが、コンパイラは制限された精度の内部表現を使用して実装する場合があります。とはいえ、すべての実装は以下の条件を満たす必要があります:
- 整数定数を少なくとも256ビットで表現すること。
- 浮動小数点定数(複素数定数の部分を含む)を少なくとも256ビットの仮数と少なくとも16ビットの符号付き二進指数で表現すること。
- 整数定数を正確に表現できない場合はエラーを出すこと。
- オーバーフローのために浮動小数点または複素数定数を表現できない場合はエラーを出すこと。
- 精度の制限のために浮動小数点または複素数定数を表現できない場合は、最も近い表現可能な定数に丸めること。
これらの要件はリテラル定数と定数式の評価結果の両方に適用されます。
変数 #
変数は値を保持するためのストレージの場所です。許容される値の集合は変数の*型*によって決定されます。
変数宣言、または関数パラメータと結果については、関数宣言や関数リテラルのシグネチャが名前付き変数のストレージを確保します。組み込み関数new
を呼び出すか、複合リテラルのアドレスを取ることで、実行時に変数のストレージを割り当てます。このような匿名変数は(暗黙的な場合もある)ポインタ間接参照を介して参照されます。
配列、スライス、および構造体型の構造化変数には、個別にアドレス指定できる要素とフィールドがあります。このような各要素は変数のように動作します。
変数の静的型(または単に型)は、その宣言で与えられた型、new
呼び出しまたは複合リテラルで提供された型、または構造化変数の要素の型です。インターフェース型の変数には、実行時に変数に割り当てられた値の型(非インターフェース型)である動的型もあります(値が型を持たない事前宣言された識別子nil
でない限り)。動的型は実行中に変化する場合がありますが、インターフェース変数に格納される値は常に変数の静的型に代入可能です。
var x interface{} // xはnilで静的型はinterface{}
var v *T // vは値nilで静的型は*T
x = 42 // xは値42で動的型はint
x = v // xは値(*T)(nil)で動的型は*T
変数の値は、式で変数を参照することによって取得されます。それは変数に代入された最新の値です。変数にまだ値が代入されていない場合、その値はその型のゼロ値です。
型 #
型は、それらの値に特有の操作とメソッドとともに一連の値を決定します。型は、もしあれば型名で示すことができますが、その型がジェネリックである場合は型引数が続く必要があります。型は型リテラルを使用して指定することもでき、これは既存の型から型を構成します。
Type = TypeName [ TypeArgs ] | TypeLit | "(" Type ")" .
TypeName = identifier | QualifiedIdent .
TypeArgs = "[" TypeList [ "," ] "]" .
TypeList = Type { "," Type } .
TypeLit = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
SliceType | MapType | ChannelType .
言語は特定の型名を事前宣言しています。その他は型宣言または型パラメータリストで導入されます。複合型—配列、構造体、ポインタ、関数、インターフェース、スライス、マップ、およびチャネル型—は型リテラルを使用して構築できます。
事前宣言型、定義型、および型パラメータは名前付き型と呼ばれます。エイリアス宣言で与えられた型が名前付き型である場合、エイリアスは名前付き型を表します。
ブール型 #
ブール型は、事前宣言された定数true
とfalse
で示されるブール真理値の集合を表します。事前宣言されたブール型はbool
です。これは定義型です。
数値型 #
整数、浮動小数点、または複素数型は、それぞれ整数、浮動小数点、または複素数の値の集合を表します。これらは集合的に数値型と呼ばれます。事前宣言されたアーキテクチャに依存しない数値型は次のとおりです:
uint8 すべての符号なし8ビット整数の集合(0〜255)
uint16 すべての符号なし16ビット整数の集合(0〜65535)
uint32 すべての符号なし32ビット整数の集合(0〜4294967295)
uint64 すべての符号なし64ビット整数の集合(0〜18446744073709551615)
int8 すべての符号付き8ビット整数の集合(-128〜127)
int16 すべての符号付き16ビット整数の集合(-32768〜32767)
int32 すべての符号付き32ビット整数の集合(-2147483648〜2147483647)
int64 すべての符号付き64ビット整数の集合(-9223372036854775808〜9223372036854775807)
float32 すべてのIEEE 754 32ビット浮動小数点数の集合
float64 すべてのIEEE 754 64ビット浮動小数点数の集合
complex64 float32の実部と虚部を持つすべての複素数の集合
complex128 float64の実部と虚部を持つすべての複素数の集合
byte uint8のエイリアス
rune int32のエイリアス
nビット整数の値はnビット幅で、2の補数演算を使用して表されます。
実装固有のサイズを持つ事前宣言された整数型のセットもあります:
uint 32ビットまたは64ビット
int uintと同じサイズ
uintptr ポインタ値の解釈されていないビットを格納するのに十分な大きさの符号なし整数
移植性の問題を避けるために、すべての数値型は定義型であり、したがってbyte
(uint8
のエイリアス)とrune
(int32
のエイリアス)を除いて区別されます。異なる数値型が式または代入で混在している場合、明示的な変換が必要です。例えば、int32
とint
は特定のアーキテクチャで同じサイズを持っていても同じ型ではありません。
文字列型 #
文字列型は文字列値の集合を表します。文字列値は(空の場合もある)バイトのシーケンスです。バイト数は文字列の長さと呼ばれ、決して負になりません。文字列は不変です:一度作成されると、文字列の内容を変更することは不可能です。事前宣言された文字列型はstring
です。これは定義型です。
文字列s
の長さは組み込み関数len
を使用して調べることができます。文字列が定数である場合、長さはコンパイル時定数です。文字列のバイトには整数インデックス0からlen(s)-1
を使ってアクセスできます。そのような要素のアドレスを取ることは不正です。s[i]
が文字列のi
番目のバイトである場合、&s[i]
は無効です。
配列型 #
配列は、要素型と呼ばれる単一の型の要素の番号付きシーケンスです。要素の数は配列の長さと呼ばれ、決して負になりません。
ArrayType = "[" ArrayLength "]" ElementType .
ArrayLength = Expression .
ElementType = Type .
長さは配列の型の一部です。それはint
型の値によって表現可能な負でない定数に評価される必要があります。配列a
の長さは組み込み関数len
を使用して調べることができます。要素には整数インデックス0からlen(a)-1
を使ってアドレスを指定できます。配列型は常に一次元ですが、多次元型を形成するために構成することができます。
[32]byte
[2*N] struct { x, y int32 }
[1000]*float64
[3][5]int
[2][2][2]float64 // [2]([2]([2]float64))と同じ
配列型T
は、T
型の要素、またはT
を構成要素として含む型(それらの含む型が配列または構造体型のみである場合)を直接的または間接的に持つことはできません。
// 無効な配列型
type (
T1 [10]T1 // T1の要素型はT1
T2 [10]struct{ f T2 } // T2は構造体の構成要素としてT2を含む
T3 [10]T4 // T3はT4の構造体の構成要素としてT3を含む
T4 struct{ f T3 } // T4は構造体内の配列T3の構成要素としてT4を含む
)
// 有効な配列型
type (
T5 [10]*T5 // T5はポインタの構成要素としてT5を含む
T6 [10]func() T6 // T6は関数型の構成要素としてT6を含む
T7 [10]struct{ f []T7 } // T7は構造体内のスライスの構成要素としてT7を含む
)
スライス型 #
スライスは基礎となる配列の連続したセグメントの記述子であり、その配列からの要素の番号付きシーケンスへのアクセスを提供します。スライス型は、その要素型の配列のすべてのスライスの集合を表します。要素の数はスライスの長さと呼ばれ、決して負になりません。初期化されていないスライスの値は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]
配列と同様に、スライスは常に一次元ですが、より高次元のオブジェクトを構築するために構成することができます。配列の配列では、内部配列は構造上常に同じ長さです。しかし、スライスのスライス(またはスライスの配列)では、内部の長さは動的に変化する可能性があります。さらに、内部スライスは個別に初期化する必要があります。
構造体型 #
構造体は、フィールドと呼ばれる名前付き要素のシーケンスであり、各フィールドには名前と型があります。フィールド名は明示的に(IdentifierList)または暗黙的に(EmbeddedField)指定できます。構造体内では、空白でないフィールド名は一意でなければなりません。
StructType = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ "*" ] TypeName [ TypeArgs ] .
Tag = string_lit .
// 空の構造体
struct {}
// 6つのフィールドを持つ構造体
struct {
x, y int
u float32
_ float32 // パディング
A *[]int
F func()
}
型は指定されているが明示的なフィールド名のないフィールドは埋め込みフィールドと呼ばれます。埋め込みフィールドは、型名T
または非インターフェース型名へのポインタ*T
として指定する必要があり、T
自体はポインタ型または型パラメータであってはなりません。修飾されていない型名がフィールド名として機能します。
// 型T1、*T2、P.T3、*P.T4の4つの埋め込みフィールドを持つ構造体
struct {
T1 // フィールド名はT1
*T2 // フィールド名はT2
P.T3 // フィールド名はT3
*P.T4 // フィールド名はT4
x, y int // フィールド名はxとy
}
フィールド名は構造体型内で一意でなければならないため、次の宣言は不正です:
struct {
T // 埋め込みフィールド*Tおよび*P.Tと競合
*T // 埋め込みフィールドTおよび*P.Tと競合
*P.T // 埋め込みフィールドTおよび*Tと競合
}
構造体x
の埋め込みフィールドのフィールドまたはメソッドf
は、x.f
がそのフィールドまたはメソッドf
を表す合法なセレクタである場合、昇格されたと呼ばれます。
昇格されたフィールドは構造体の通常のフィールドのように動作しますが、構造体の複合リテラル内でフィールド名として使用することはできません。
構造体型S
と型名T
が与えられた場合、昇格されたメソッドは次のように構造体のメソッドセットに含まれます:
S
が埋め込みフィールドT
を含む場合、S
と*S
のメソッドセットの両方に、レシーバT
を持つ昇格されたメソッドが含まれます。*S
のメソッドセットには、レシーバ*T
を持つ昇格されたメソッドも含まれます。S
が埋め込みフィールド*T
を含む場合、S
と*S
のメソッドセットの両方に、レシーバT
または*T
を持つ昇格されたメソッドが含まれます。
フィールド宣言の後にはオプションの文字列リテラルタグが続くことがあり、これは対応するフィールド宣言内のすべてのフィールドの属性になります。空のタグ文字列は存在しないタグと同等です。タグはリフレクションインターフェースを通じて可視化され、構造体の型アイデンティティに参加しますが、それ以外は無視されます。
struct {
x, y float64 "" // 空のタグ文字列は存在しないタグと同様
name string "任意の文字列がタグとして許可される"
_ [4]byte "ceci n'est pas un champ de structure"
}
// TimeStampプロトコルバッファに対応する構造体
// タグ文字列はプロトコルバッファのフィールド番号を定義します。
// これらはreflectパッケージで概説されている規約に従っています。
struct {
microsec uint64 `protobuf:"1"`
serverIP6 uint64 `protobuf:"2"`
}
構造体型T
は、型T
のフィールド、または含む型が配列または構造体型のみである場合、直接的または間接的に構成要素としてT
を含む型のフィールドを含むことはできません。
// 無効な構造体型
type (
T1 struct{ T1 } // T1はT1型のフィールドを含む
T2 struct{ f [10]T2 } // T2は配列の構成要素としてT2を含む
T3 struct{ T4 } // T3は構造体T4内の配列の構成要素としてT3を含む
T4 struct{ f [10]T3 } // T4は配列内の構造体T3の構成要素としてT4を含む
)
// 有効な構造体型
type (
T5 struct{ f *T5 } // T5はポインタの構成要素としてT5を含む
T6 struct{ f func() T6 } // T6は関数型の構成要素としてT6を含む
T7 struct{ f [10][]T7 } // T7は配列内のスライスの構成要素としてT7を含む
)
ポインタ型 #
ポインタ型は、特定の型(ポインタの基底型と呼ばれる)の変数へのすべてのポインタの集合を表します。初期化されていないポインタの値は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つある場合は、括弧なしの型として記述できます。
関数シグネチャの最後の入力パラメータには、...
を前置した型を持つことができます。このようなパラメータを持つ関数は可変引数と呼ばれ、そのパラメータに対してゼロ個以上の引数で呼び出すことができます。
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つ以上の型項の和集合です。型項は単一の型または単一の基底型のいずれかです。
基本インターフェース #
最も基本的な形式では、インターフェースは(空の場合もある)メソッドのリストを指定します。このようなインターフェースによって定義される型集合は、それらのメソッドをすべて実装する型の集合であり、対応するメソッドセットはインターフェースによって指定されたメソッドのみで構成されます。型集合がメソッドのリストだけで完全に定義できるインターフェースは、基本インターフェースと呼ばれます。
// シンプルなFileインターフェース
interface {
Read([]byte) (int, error)
Write([]byte) (int, error)
Close() error
}
明示的に指定された各メソッドの名前は一意で空白であってはなりません。
interface {
String() string
String() string // 不正:Stringが一意でない
_(x int) // 不正:メソッドは空白でない名前を持つ必要がある
}
複数の型が同じインターフェースを実装することがあります。例えば、2つの型S1
とS2
が以下のメソッドセットを持つ場合:
func (p T) Read(p []byte) (n int, err error)
func (p T) Write(p []byte) (n int, err error)
func (p T) Close() error
(ここでT
はS1
またはS2
のいずれかを表します)S1
とS2
がどのような他のメソッドを持っていたり共有していたりしても、File
インターフェースはS1
とS2
の両方によって実装されます。
インターフェースの型集合のメンバーであるすべての型は、そのインターフェースを実装します。任意の型は複数の異なるインターフェースを実装することがあります。例えば、すべての型はすべての(非インターフェース)型の集合を表す空インターフェースを実装します:
interface{}
便宜上、事前宣言された型any
は空インターフェースのエイリアスです [Go 1.18]。
同様に、次のインターフェース仕様を考えてみましょう。これはLocker
というインターフェースを定義するために型宣言内に表示されます:
type Locker interface {
Lock()
Unlock()
}
S1
とS2
も以下を実装している場合:
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のメソッドは、Read、Write、Closeです。
type ReadWriter interface {
Reader // ReaderのメソッドをReadWriterのメソッドセットに含める
Writer // WriterのメソッドをReadWriterのメソッドセットに含める
}
インターフェースを埋め込む場合、同じ名前のメソッドは同一のシグネチャを持つ必要があります。
type ReadCloser interface {
Reader // ReaderのメソッドをReadCloserのメソッドセットに含める
Close() // 不正:Reader.CloseとCloseのシグネチャが異なる
}
一般的なインターフェース #
最も一般的な形式では、インターフェース要素は任意の型項T
、または基底型T
を指定する~T
形式の項、またはt₁|t₂|…|tₙ
形式の項の和集合であることもあります [Go 1.18]。メソッド仕様とともに、これらの要素はインターフェースの型集合を次のように正確に定義することを可能にします:
- 空インターフェースの型集合は、すべての非インターフェース型の集合です。
- 空でないインターフェースの型集合は、そのインターフェース要素の型集合の交差です。
- メソッド仕様の型集合は、メソッドセットにそのメソッドを含むすべての非インターフェース型の集合です。
- 非インターフェース型項の型集合は、その型だけからなる集合です。
~T
形式の項の型集合は、基底型がT
であるすべての型の集合です。- 項の和集合
t₁|t₂|…|tₙ
の型集合は、各項の型集合の和集合です。
「すべての非インターフェース型の集合」という量化は、プログラム内で宣言されたすべての(非インターフェース)型だけでなく、すべての可能なプログラム内のすべての可能な型を指し、したがって無限です。同様に、特定のメソッドを実装するすべての非インターフェース型の集合が与えられた場合、それらの型のメソッドセットの交差は、プログラム内のすべての型が常にそのメソッドを別のメソッドと対にしていても、ちょうどそのメソッドを含みます。
構成上、インターフェースの型集合はインターフェース型を含みません。
// int型のみを表すインターフェース
interface {
int
}
// 基底型がintのすべての型を表すインターフェース
interface {
~int
}
// 基底型がintでStringメソッドを実装するすべての型を表すインターフェース
interface {
~int
String() string
}
// 空の型集合を表すインターフェース:intかつstringである型は存在しない
interface {
int
string
}
~T
形式の項では、T
の基底型はそれ自体でなければならず、T
はインターフェースであってはなりません。
type MyInt int
interface {
~[]byte // []byteの基底型はそれ自体
~MyInt // 不正:MyIntの基底型はMyIntではない
~error // 不正:errorはインターフェース
}
和集合要素は型集合の和集合を表します:
// Floatインターフェースはすべての浮動小数点型を表す
// (基底型がfloat32またはfloat64である名前付き型を含む)
type Float interface {
~float32 | ~float64
}
T
または~T
形式の項における型T
は型パラメータであってはならず、すべての非インターフェース項の型集合は互いに素でなければなりません(型集合の対ごとの交差は空でなければなりません)。型パラメータP
が与えられた場合:
interface {
P // 不正:Pは型パラメータ
int | ~P // 不正:Pは型パラメータ
~int | MyInt // 不正:~intとMyIntの型集合は互いに素ではない(~intはMyIntを含む)
float32 | Float // 型集合が重複しているが、Floatはインターフェース
}
実装上の制限:
和集合(複数の項を持つ)は、事前宣言された識別子comparable
、またはメソッドを指定するインターフェース、またはcomparable
を埋め込むインターフェース、またはメソッドを指定するインターフェースを含むことはできません。
基本でないインターフェースは、型制約として、または制約として使用される他のインターフェースの要素としてのみ使用できます。それらは値や変数の型、または他の非インターフェース型の構成要素であることはできません。
var x Float // 不正:Floatは基本インターフェースではない
var x interface{} = Float(nil) // 不正
type Floatish struct {
f Float // 不正
}
インターフェース型T
は、直接的または間接的にT
である、T
を含む、またはT
を埋め込む型要素を埋め込むことはできません。
// 不正:BadはBad自身を埋め込むことはできない
type Bad interface {
Bad
}
// 不正:Bad1はBad2を使って自身を埋め込むことはできない
type Bad1 interface {
Bad2
}
type Bad2 interface {
Bad1
}
// 不正:Bad3はBad3を含む和集合を埋め込むことはできない
type Bad3 interface {
~int | ~string | Bad3
}
// 不正:Bad4は要素型としてBad4を持つ配列を埋め込むことはできない
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
を使用して調べることができ、実行中に変化する可能性があります。要素は代入文を使用して実行中に追加でき、インデックス式で取得できます。組み込み関数delete
とclear
で削除できます。
新しい空のマップ値は、組み込み関数make
を使用して作成され、マップ型とオプションの容量ヒントを引数として取ります:
make(map[string]int)
make(map[string]int, 100)
初期容量はそのサイズを制限しません:マップは、nil
マップを除いて、格納される項目の数に対応するように成長します。nil
マップは、要素を追加できないことを除いて、空のマップと同等です。
チャネル型 #
チャネルは、並行実行関数が指定された要素型の値を送信および受信することによって通信するためのメカニズムを提供します。初期化されていないチャネルの値はnil
です。
ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .
オプションの<-
演算子はチャネルの方向(送信または受信)を指定します。方向が指定されている場合、チャネルは一方向であり、そうでない場合は双方向です。チャネルは代入または明示的な変換によって、送信のみまたは受信のみに制限できます。
chan T // 型Tの値を送信および受信するために使用できる
chan<- float64 // float64を送信するためにのみ使用できる
<-chan int // intを受信するためにのみ使用できる
<-
演算子は可能な限り左端のchan
に関連付けられます:
chan<- chan int // chan<- (chan int)と同じ
chan<- <-chan int // chan<- (<-chan int)と同じ
<-chan <-chan int // <-chan (<-chan int)と同じ
chan (<-chan int)
新しい初期化されたチャネル値は、組み込み関数make
を使用して作成でき、チャネル型とオプションの容量を引数として取ります:
make(chan int, 100)
要素数での容量は、チャネル内のバッファのサイズを設定します。容量がゼロまたは存在しない場合、チャネルはバッファリングされず、送信者と受信者の両方が準備できている場合にのみ通信が成功します。それ以外の場合、チャネルはバッファリングされ、バッファが満杯でない(送信の場合)または空でない(受信の場合)場合、通信はブロックせずに成功します。nil
チャネルは通信の準備ができていません。
チャネルは組み込み関数close
で閉じることができます。受信演算子の複数値の代入形式は、受信した値がチャネルが閉じられる前に送信されたかどうかを報告します。
単一のチャネルは、さらに同期せずに任意の数のゴルーチンによって、送信文、受信操作、および組み込み関数cap
とlen
の呼び出しで使用できます。チャネルは先入れ先出しのキューとして機能します。たとえば、あるゴルーチンがチャネルに値を送信し、2番目のゴルーチンがそれらを受信する場合、値は送信された順序で受信されます。
型と値の特性 #
値の表現 #
事前宣言された型(以下のインターフェースany
とerror
を参照)、配列、および構造体の値は自己完結型です:そのような各値はそのデータのすべての完全なコピーを含み、そのような型の変数は値全体を格納します。たとえば、配列変数は配列のすべての要素のストレージ(変数)を提供します。それぞれのゼロ値は値の型に固有であり、決してnil
ではありません。
nilではないポインタ、関数、スライス、マップ、およびチャネルの値には、複数の値によって共有される可能性のある基礎となるデータへの参照が含まれています:
- ポインタ値は、ポインタの基底型の値を保持する変数への参照です。
- 関数値は、(場合によっては匿名の)関数とその環境への参照を含みます。
- スライス値は、スライスの長さ、容量、および基底配列への参照を含みます。
- マップまたはチャネル値は、マップまたはチャネルの実装固有のデータ構造への参照です。
インターフェース値は、インターフェースの動的型に応じて、自己完結型であるか、基礎となるデータへの参照を含むことがあります。事前宣言された識別子nil
は、参照を含むことができる型のゼロ値です。
複数の値が基礎となるデータを共有する場合、ある値を変更すると別の値も変更される可能性があります。たとえば、スライスの要素を変更すると、配列を共有するすべてのスライスの基底配列のその要素が変更されます。
基底型 #
各型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) { … }
string
、A1
、A2
、B1
、およびB2
の基底型はstring
です。[]B1
、B3
、およびB4
の基底型は[]B1
です。P
の基底型はinterface{}
です。
型の同一性 #
二つの型は同一(「同じ」)であるか、または異なるかのいずれかです。
名前付き型は常に他の型とは異なります。それ以外の場合、二つの型は、その基底型リテラルが構造的に等価である場合に同一です;つまり、それらは同じリテラル構造を持ち、対応するコンポーネントが同一の型を持ちます。詳細には:
- 二つの配列型は、同一の要素型と同じ配列の長さを持つ場合に同一です。
- 二つのスライス型は、同一の要素型を持つ場合に同一です。
- 二つの構造体型は、同じフィールドのシーケンスを持ち、対応するフィールドのペアが同じ名前、同一の型、同一のタグを持ち、かつ両方が埋め込まれているか、両方が埋め込まれていない場合に同一です。異なるパッケージからのエクスポートされていないフィールド名は常に異なります。
- 二つのポインタ型は、同一のベース型を持つ場合に同一です。
- 二つの関数型は、同じ数のパラメータと結果値を持ち、対応するパラメータと結果の型が同一であり、かつ両方の関数が可変引数を持つか、両方とも持たない場合に同一です。パラメータと結果の名前は一致する必要はありません。
- 二つのインターフェース型は、同じ型セットを定義する場合に同一です。
- 二つのマップ型は、同一のキーと要素の型を持つ場合に同一です。
- 二つのチャネル型は、同一の要素型と同じ方向を持つ場合に同一です。
- 二つのインスタンス化された型は、それらの定義された型とすべての型引数が同一である場合に同一です。
以下の宣言が与えられた場合:
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
B0
とB1
は、それらが別々の型定義によって作成された新しい型であるため異なります;func(int, float64) *B0
とfunc(x int, y float64) *[]string
は、B0
が[]string
とは異なるため異なります;またP1
とP2
は、それらが異なる型パラメータであるため異なります。D0[int, string]
とstruct{ x int; y string }
は、前者がインスタンス化された定義型であるのに対し、後者は型リテラルであるため異なります(ただし、それらは依然として代入可能です)。
代入可能性 #
型V
の値x
が型T
の変数に代入可能である(「x
はT
に代入可能」)のは、以下の条件のいずれかが適用される場合です:
V
とT
が同一である。V
とT
が同一の基底型を持ち、型パラメータではなく、かつV
またはT
の少なくとも一方が名前付き型ではない。V
とT
が同一の要素型を持つチャネル型であり、V
が双方向チャネルであり、かつV
またはT
の少なくとも一方が名前付き型ではない。T
がインターフェース型であり、型パラメータではなく、かつx
がT
を実装している。x
が事前宣言された識別子nil
であり、T
がポインタ型、関数型、スライス型、マップ型、チャネル型、またはインターフェース型であり、型パラメータではない。x
が型T
の値によって表現可能な型付けされていない定数である。
さらに、x
の型V
またはT
が型パラメータである場合、x
が型T
の変数に代入可能なのは、以下の条件のいずれかが適用される場合です:
x
が事前宣言された識別子nil
であり、T
が型パラメータであり、かつx
がT
の型セット内のすべての型に代入可能である。V
が名前付き型ではなく、T
が型パラメータであり、かつx
がT
の型セット内のすべての型に代入可能である。V
が型パラメータであり、T
が名前付き型ではなく、かつV
の型セット内のすべての型の値がT
に代入可能である。
表現可能性 #
定数 x
は、型パラメータではない型 T
の値によって表現可能であるのは、以下の条件のいずれかが適用される場合です:
x
がT
によって決定される値の集合内にある。T
が浮動小数点型であり、x
がオーバーフローなしにT
の精度に丸めることができる。丸めは IEEE 754 の偶数丸めルールを使用しますが、IEEE の負のゼロはさらに符号なしゼロに簡略化されます。定数値は決して IEEE の負のゼロ、NaN、または無限大にはならないことに注意してください。T
が複素数型であり、x
の成分real(x)
とimag(x)
がT
の成分型(float32
またはfloat64
)の値によって表現可能である。
T
が型パラメータである場合、x
は T
の型セット内のすべての型の値によって x
が表現可能である場合に、型 T
の値によって表現可能です。
x T x は T の値によって表現可能である理由
'a' byte 97 はバイト値の集合内にある
97 rune rune は int32 のエイリアスであり、97 は 32 ビット整数の集合内にある
"foo" string "foo" は文字列値の集合内にある
1024 int16 1024 は 16 ビット整数の集合内にある
42.0 byte 42 は符号なし 8 ビット整数の集合内にある
1e10 uint64 10000000000 は符号なし 64 ビット整数の集合内にある
2.718281828459045 float32 2.718281828459045 は 2.7182817 に丸められ、float32 値の集合内にある
-1e-1000 float64 -1e-1000 は IEEE の -0.0 に丸められ、さらに 0.0 に簡略化される
0i int 0 は整数値である
(42 + 0i) float32 42.0(虚部がゼロ)は float32 値の集合内にある
x T x が T の値によって表現可能ではない理由
0 bool 0 はブール値の集合内にない
'a' string 'a' はルーンであり、文字列値の集合内にない
1024 byte 1024 は符号なし 8 ビット整数の集合内にない
-1 uint16 -1 は符号なし 16 ビット整数の集合内にない
1.1 int 1.1 は整数値ではない
42i float32 (0 + 42i) は float32 値の集合内にない
1e1000 float64 1e1000 は丸めた後に IEEE の +Inf にオーバーフローする
メソッドセット #
型のメソッドセットは、その型のオペランドに対して呼び出し可能なメソッドを決定します。すべての型には(空の可能性もある)メソッドセットが関連付けられています:
- 定義型
T
のメソッドセットは、レシーバ型T
で宣言されたすべてのメソッドから構成されます。 - 定義型
T
へのポインタのメソッドセット(ここでT
はポインタでもインターフェースでもない)は、レシーバ*T
またはT
で宣言されたすべてのメソッドの集合です。 - インターフェース型のメソッドセットは、インターフェースの型セット内の各型のメソッドセットの交差です(結果のメソッドセットは通常、インターフェースで宣言されたメソッドの集合です)。
埋め込みフィールドを含む構造体(および構造体へのポインタ)にはさらに規則が適用され、構造体型のセクションで説明されています。その他の型には空のメソッドセットがあります。
メソッドセットでは、各メソッドは一意の非ブランクメソッド名を持つ必要があります。
ブロック #
ブロックは、対応する波括弧内にある、宣言と文の(空である可能性もある)シーケンスです。
Block = "{" StatementList "}" .
StatementList = { Statement ";" } .
ソースコードの明示的なブロックに加えて、暗黙的なブロックもあります:
- ユニバースブロックは、すべてのGoソーステキストを包含します。
- 各パッケージは、そのパッケージのすべてのGoソーステキストを含むパッケージブロックを持ちます。
- 各ファイルは、そのファイル内のすべてのGoソーステキストを含むファイルブロックを持ちます。
- 各“if”文、“for”文、および“switch”文は、それぞれが独自の暗黙的なブロック内にあると見なされます。
- “switch”文または“select”文の各節は、暗黙的なブロックとして機能します。
ブロックは入れ子になり、スコープに影響を与えます。
宣言とスコープ #
宣言は、空白でない識別子を定数、型、型パラメータ、変数、関数、ラベル、またはパッケージに結び付けます。プログラム内のすべての識別子は宣言されなければなりません。同じブロック内で識別子を2回宣言することはできず、ファイルブロックとパッケージブロックの両方で識別子を宣言することもできません。
空白識別子は、宣言内で他の識別子のように使用できますが、バインディングを導入しないため宣言されません。パッケージブロックでは、識別子init
はinit
関数宣言にのみ使用でき、空白識別子と同様に新しいバインディングを導入しません。
Declaration = ConstDecl | TypeDecl | VarDecl .
TopLevelDecl = Declaration | FunctionDecl | MethodDecl .
宣言された識別子のスコープは、その識別子が指定された定数、型、変数、関数、ラベル、またはパッケージを表すソーステキストの範囲です。
Goはブロックを使用する字句スコープを持ちます:
定義済み識別子のスコープはユニバースブロックです。
トップレベル(関数外)で宣言された定数、型、変数、関数(メソッドは除く)を表す識別子のスコープはパッケージブロックです。
インポートされたパッケージのパッケージ名のスコープは、インポート宣言を含むファイルのファイルブロックです。
メソッドレシーバ、関数パラメータ、または結果変数を表す識別子のスコープは関数本体です。
関数の型パラメータまたはメソッドレシーバによって宣言された識別子のスコープは、関数の名前の後から始まり、関数本体の終わりで終わります。
型の型パラメータを表す識別子のスコープは、型の名前の後から始まり、TypeSpecの終わりで終わります。
関数内で宣言された定数または変数識別子のスコープは、ConstSpecまたはVarSpec(短い変数宣言の場合はShortVarDecl)の終わりから始まり、最も内側の包含ブロックの終わりで終わります。
関数内で宣言された型識別子のスコープは、TypeSpecの識別子から始まり、最も内側の包含ブロックの終わりで終わります。
ブロック内で宣言された識別子は、内部ブロックで再宣言できます。内部宣言の識別子がスコープ内にある間、それは内部宣言によって宣言されたエンティティを表します。
パッケージ句は宣言ではありません。パッケージ名はどのスコープにも現れません。その目的は、同じパッケージに属するファイルを識別し、インポート宣言のデフォルトパッケージ名を指定することです。
ラベルスコープ #
ラベルはラベル付きステートメントによって宣言され、“break”、“continue”、“goto”ステートメントで使用されます。使用されないラベルを定義することは違法です。他の識別子とは異なり、ラベルはブロックスコープではなく、ラベルではない識別子と競合しません。ラベルのスコープは、それが宣言されている関数の本体であり、ネストされた関数の本体は除外されます。
空白識別子 #
空白識別子はアンダースコア文字_
で表されます。通常の(空白でない)識別子の代わりに匿名のプレースホルダとして機能し、宣言、オペランド、代入文で特別な意味を持ちます。
定義済み識別子 #
以下の識別子はユニバースブロックで暗黙的に宣言されています [Go 1.18] [Go 1.21]:
型:
any bool byte comparable
complex64 complex128 error float32 float64
int int8 int16 int32 int64 rune string
uint uint8 uint16 uint32 uint64 uintptr
定数:
true false iota
ゼロ値:
nil
関数:
append cap clear close complex copy delete imag len
make max min new panic print println real recover
エクスポートされた識別子 #
識別子は、別のパッケージからアクセスできるようにエクスポートされることがあります。以下の両方の条件を満たす場合、識別子はエクスポートされます:
それ以外のすべての識別子はエクスポートされません。
識別子の一意性 #
識別子のセットが与えられた場合、そのセット内の他のすべての識別子と異なる場合、識別子は一意と呼ばれます。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 // 型付けされていない浮動小数点定数
const (
size int64 = 1024
eof = -1 // 型付けされていない整数定数
)
const a, b, c = 3, 4, "foo" // a = 3, b = 4, c = "foo", 型付けされていない整数と文字列定数
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 // この定数はエクスポートされません
)
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, 未使用)
d = 1 << iota // d == 8 (iota == 3)
)
const (
u = iota * 42 // u == 0 (型付けされていない整数定数)
v float64 = iota * 42 // v == 42.0 (float64定数)
w = iota * 42 // w == 84 (型付けされていない整数定数)
)
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, 未使用)
bit3, mask3 // bit3 == 8, mask3 == 7 (iota == 3)
)
この最後の例は、最後の非空の式リストの暗黙的な繰り返しを利用しています。
型宣言 #
型宣言は識別子(型名)を型に結び付けます。型宣言には2つの形式があります:エイリアス宣言と型定義です。
TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
TypeSpec = AliasDecl | TypeDef .
エイリアス宣言 #
エイリアス宣言は識別子を指定された型に結び付けます [Go 1.9]。
AliasDecl = identifier [ TypeParameters ] "=" Type .
識別子のスコープ内では、それは指定された型のエイリアスとして機能します。
type (
nodeList = []*Node // nodeListと[]*Nodeは同一の型です
Polar = polar // PolarとpolarはPolarは同じ型を表します
)
エイリアス宣言が型パラメータを指定する場合 [Go 1.24]、型名はジェネリックエイリアスを表します。ジェネリックエイリアスは使用時にインスタンス化されなければなりません。
type set[P comparable] = map[P]bool
エイリアス宣言では、指定された型は型パラメータであってはなりません。
type A[P any] = P // 不正: Pは型パラメータです
型定義 #
型定義は、指定された型と同じ基底型および操作を持つ新しい異なる型を作成し、識別子(型名)をそれに結び付けます。
TypeDef = identifier [ TypeParameters ] Type .
新しい型は定義された型と呼ばれます。それは、作成元の型を含む他のどの型とも異なります。
type (
Point struct{ x, y float64 } // PointとPとstruct{ x, y float64 }は異なる型です
polar Point // polarとPointは異なる型を表します
)
type TreeNode struct {
left, right *TreeNode
value any
}
type Block interface {
BlockSize() int
Encrypt(src, dst []byte)
Decrypt(src, dst []byte)
}
定義された型にはメソッドを関連付けることができます。指定された型に結び付けられたメソッドは継承しませんが、インタフェース型または複合型の要素のメソッドセットは変更されません:
// Mutexは2つのメソッド、LockとUnlockを持つデータ型です。
type Mutex struct { /* Mutexフィールド */ }
func (m *Mutex) Lock() { /* Lockの実装 */ }
func (m *Mutex) Unlock() { /* Unlockの実装 */ }
// NewMutexはMutexと同じ構成を持ちますが、そのメソッドセットは空です。
type NewMutex Mutex
// PtrMutexの基底型*Mutexのメソッドセットは変更されませんが、
// PtrMutexのメソッドセットは空です。
type PtrMutex *Mutex
// *PrintableMutexのメソッドセットには、埋め込みフィールドMutexに結び付けられた
// メソッドLockとUnlockが含まれます。
type PrintableMutex struct {
Mutex
}
// MyBlockは、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 // 不正: Pは型パラメータです
func f[T any]() {
type L T // 不正: Tは囲む関数によって宣言された型パラメータです
}
ジェネリック型にもメソッドを関連付けることができます。この場合、メソッドレシーバーはジェネリック型定義に存在するのと同じ数の型パラメータを宣言する必要があります。
// メソッドLenは連結リストlの要素数を返します。
func (l *List[T]) Len() int { … }
型パラメータ宣言 #
型パラメータリストは、ジェネリック関数または型宣言の型パラメータを宣言します。型パラメータリストは通常の関数パラメータリストのように見えますが、型パラメータ名はすべて存在する必要があり、リストは括弧ではなく角括弧で囲まれています [Go 1.18]。
TypeParameters = "[" TypeParamList [ "," ] "]" .
TypeParamList = TypeParamDecl { "," TypeParamDecl } .
TypeParamDecl = IdentifierList TypeConstraint .
リスト内のすべての非空の名前は一意でなければなりません。各名前は型パラメータを宣言し、これは宣言内で(まだ)未知の型のプレースホルダとして機能する新しい異なる名前付き型です。型パラメータは、ジェネリック関数または型のインスタンス化時に型引数に置き換えられます。
[P any]
[S interface{ ~[]byte|string }]
[S ~[]E, E any]
[P Constraint[int]]
[_ any]
通常の関数パラメータがパラメータ型を持つのと同様に、各型パラメータには型制約と呼ばれる対応する(メタ)型があります。
ジェネリック型の型パラメータリストが、テキストP C
が有効な式を形成するような制約C
を持つ単一の型パラメータP
を宣言する場合、構文解析の曖昧さが生じます:
type T[P *C] …
type T[P (C)] …
type T[P *C|Q] …
…
これらのまれなケースでは、型パラメータリストは式と区別できず、型宣言は配列型宣言として解析されます。この曖昧さを解決するには、制約をインタフェースに埋め込むか、末尾のカンマを使用します:
type T[P interface{*C}] …
type T[P *C,] …
型パラメータは、ジェネリック型に関連付けられたメソッド宣言のレシーバー指定によっても宣言できます。
ジェネリック型T
の型パラメータリスト内では、型制約は(直接的に、または別のジェネリック型の型パラメータリストを通じて間接的に)T
を参照することはできません。
type T1[P T1[P]] … // 不正: T1は自身を参照します
type T2[P interface{ T2[int] }] … // 不正: T2は自身を参照します
type T3[P interface{ m(T3[int])}] … // 不正: T3は自身を参照します
type T4[P T5[P]] … // 不正: T4はT5を参照し、
type T5[P T4[P]] … // T5はT4を参照します
type T6[P int] struct{ f *T6[P] } // 正常: T6への参照は型パラメータリスト内にありません
型制約 #
型制約は、それぞれの型パラメータに許容される型引数のセットを定義し、その型パラメータの値でサポートされる操作を制御するインタフェースです [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 // 不正: ~intは型パラメータリスト内にありません
定義済みインタフェース型 comparable
は、厳密に比較可能なすべての非インタフェース型のセットを表します [Go 1.18]。
型パラメータではないインタフェースは比較可能ですが、厳密に比較可能ではないため、comparable
を実装しません。ただし、それらはcomparable
を満たします。
int // comparableを実装します(intは厳密に比較可能です)
[]byte // comparableを実装しません(スライスは比較できません)
interface{} // comparableを実装しません(上記参照)
interface{ ~int | ~string } // 型パラメータのみ: comparableを実装します(int、string型は厳密に比較可能です)
interface{ comparable } // 型パラメータのみ: comparableを実装します(comparableは自身を実装します)
interface{ ~int | ~[]byte } // 型パラメータのみ: comparableを実装しません(スライスは比較できません)
interface{ ~struct{ any } } // 型パラメータのみ: comparableを実装しません(フィールドanyは厳密に比較可能ではありません)
comparable
インタフェースおよびcomparable
を(直接的または間接的に)埋め込むインタフェースは、型制約としてのみ使用できます。それらは値または変数の型、または他の非インタフェース型のコンポーネントになることはできません。
型制約の満足 #
型引数T
は、T
がC
によって定義された型セットの要素である場合、つまりT
がC
を実装する場合、型制約C
を満足します。例外として、厳密に比較可能な型制約は、比較可能な(必ずしも厳密に比較可能ではない)型引数によって満たされることもあります [Go 1.20]。より正確には:
型Tは、次の場合に制約C
を満足します:
型引数 型制約 // 制約満足
int interface{ ~int } // 満足: intはinterface{ ~int }を実装します
string comparable // 満足: stringはcomparableを実装します(stringは厳密に比較可能です)
[]byte comparable // 満足しない: スライスは比較できません
any interface{ comparable; int } // 満足しない: anyはinterface{ int }を実装しません
any comparable // 満足: anyは比較可能で基本インタフェースanyを実装します
struct{f any} comparable // 満足: struct{f any}は比較可能で基本インタフェースanyを実装します
any interface{ comparable; m() } // 満足しない: anyは基本インタフェースinterface{ m() }を実装しません
interface{ m() } interface{ comparable; m() } // 満足: interface{ m() }は比較可能で基本インタフェース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] // マップ検索; "found"にのみ興味がある
式のリストが与えられた場合、変数は代入文のルールに従って式で初期化されます。そうでない場合、各変数はそのゼロ値に初期化されます。
型が存在する場合、各変数にはその型が与えられます。そうでない場合、各変数は代入における対応する初期化値の型が与えられます。その値が型付けされていない定数である場合、まず暗黙的にそのデフォルト型に変換されます。型付けされていないブール値の場合、まず暗黙的にbool
型に変換されます。定義済み識別子nil
は、明示的な型のない変数を初期化するために使用することはできません。
var d = math.Sin(0.5) // dはfloat64です
var i = 42 // iはintです
var t, ok = x.(T) // tはT、okはboolです
var n = nil // 不正
実装上の制限:コンパイラは、変数が使用されない場合、関数本体内での変数宣言を不正にすることがあります。
短い変数宣言 #
短い変数宣言は以下の構文を使用します:
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()は接続されたFileのペアとエラー(あれば)を返します
_, y, _ := coord(p) // coord()は3つの値を返します; y座標にのみ興味があります
通常の変数宣言とは異なり、短い変数宣言は、元々同じブロック内(またはブロックが関数本体の場合はパラメータリスト)で同じ型で宣言されており、少なくとも1つの非空白変数が新しい場合に変数を再宣言することができます。結果として、再宣言は複数変数の短い宣言でのみ表示できます。再宣言は新しい変数を導入せず、単に元の変数に新しい値を割り当てるだけです。:=
の左側の非空白変数名は一意でなければなりません。
field1, offset := nextField(str, 0)
field2, offset := nextField(str, offset) // offsetを再宣言します
x, y, x := 1, 2, 3 // 不正: :=の左側でxが繰り返されています
短い変数宣言は関数内でのみ表示できます。“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
}
}
// 不正: return文が欠けています
}
関数宣言が型パラメータを指定する場合、関数名はジェネリック関数を表します。ジェネリック関数は、呼び出されたり値として使用されたりする前にインスタンス化されなければなりません。
func min[T ~int|~float64](x, y T) T {
if x < y {
return x
}
return y
}
型パラメータのない関数宣言は本体を省略することができます。このような宣言は、アセンブリルーチンなど、Go外で実装される関数のシグネチャを提供します。
func flushICache(begin, end uintptr) // 外部で実装
メソッド宣言 #
メソッドはレシーバーを持つ関数です。メソッド宣言は識別子(メソッド名)をメソッドに結び付け、メソッドをレシーバーの基底型に関連付けます。
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
を持つメソッドLength
とScale
を基底型Point
にバインドします。
レシーバー基底型がジェネリック型の場合、レシーバー指定はメソッドが使用するための対応する型パラメーターを宣言する必要があります。これにより、レシーバー型パラメーターがメソッドで利用可能になります。構文的には、この型パラメーター宣言はレシーバー基底型のインスタンス化のように見えます:型引数は宣言される型パラメーターを表す識別子でなければならず、レシーバー基底型の各型パラメーターに対して1つずつです。型パラメーター名はレシーバー基底型定義の対応するパラメーター名と一致する必要はなく、すべての非空白パラメーター名はレシーバーパラメーターセクションとメソッドシグネチャで一意でなければなりません。レシーバー型パラメーター制約はレシーバー基底型定義によって暗示されます:対応する型パラメーターには対応する制約があります。
type Pair[A, B any] struct {
a A
b B
}
func (p Pair[A, B]) Swap() Pair[B, A] { … } // レシーバーはA, Bを宣言します
func (p Pair[First, _]) First() First { … } // レシーバーはFirstを宣言し、PairのAに対応します
レシーバー型が(ポインタを介して)エイリアスで表される場合、エイリアスはジェネリックであってはならず、インスタンス化されたジェネリック型を表してはなりません。これは、別のエイリアスを介して直接的にも間接的にも、ポインタの間接参照に関係なく適用されます。
type GPoint[P any] = Point
type HPoint = *GPoint[int]
type IPair = Pair[int, int]
func (*GPoint[P]) Draw(P) { … } // 不正: エイリアスはジェネリックであってはなりません
func (HPoint) Draw(P) { … } // 不正: エイリアスはインスタンス化された型GPoint[int]を表してはなりません
func (*IPair) Second() int { … } // 不正: エイリアスはインスタンス化された型Pair[int, int]を表してはなりません
式 #
式は演算子と関数をオペランドに適用することによって値の計算を指定します。
オペランド #
オペランドは式の基本的な値を表します。オペランドは、リテラル、(場合によっては修飾された)空白でない識別子(定数、変数、または関数を示す)、あるいは括弧で囲まれた式です。
Operand = Literal | OperandName [ TypeArgs ] | "(" Expression ")" .
Literal = BasicLit | CompositeLit | FunctionLit .
BasicLit = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
OperandName = identifier | QualifiedIdent .
ジェネリック関数を示すオペランド名の後には型引数のリストが続くことがあります。結果として得られるオペランドはインスタンス化された関数です。
空白識別子は、代入文の左辺においてのみオペランドとして表示されることがあります。
実装上の制約:コンパイラは、オペランドの型が空の型セットを持つ型パラメータである場合にエラーを報告する必要はありません。そのような型パラメータを持つ関数はインスタンス化できません。インスタンス化の試みはインスタンス化のサイトでエラーになります。
修飾識別子 #
修飾識別子はパッケージ名の接頭辞で修飾された識別子です。パッケージ名と識別子の両方が空白であってはなりません。
QualifiedIdent = PackageName "." identifier .
修飾識別子は、異なるパッケージの識別子にアクセスします。そのパッケージはインポートされている必要があります。識別子はエクスポートされており、そのパッケージのパッケージブロックで宣言されている必要があります。
math.Sin // mathパッケージのSin関数を表す
複合リテラル #
複合リテラルは、評価されるたびに構造体、配列、スライス、およびマップの新しい値を構築します。これらは、リテラルの型の後に中括弧で囲まれた要素のリストが続くものです。各要素の前に、対応するキーをオプションで付けることができます。
CompositeLit = LiteralType LiteralValue .
LiteralType = StructType | ArrayType | "[" "..." "]" ElementType |
SliceType | MapType | TypeName [ TypeArgs ] .
LiteralValue = "{" [ ElementList [ "," ] ] "}" .
ElementList = KeyedElement { "," KeyedElement } .
KeyedElement = [ Key ":" ] Element .
Key = FieldName | Expression | LiteralValue .
FieldName = identifier .
Element = Expression | LiteralValue .
LiteralTypeが型パラメータでない限り、その基底型は構造体、配列、スライス、またはマップ型でなければなりません(型がTypeNameとして与えられる場合を除き、構文はこの制約を強制します)。LiteralTypeが型パラメータである場合、その型セット内のすべての型は同じ基底型を持ち、それは有効な複合リテラル型でなければなりません。要素とキーの型は、型T
の各フィールド、要素、およびキーの型に代入可能でなければなりません。追加の変換はありません。キーは構造体リテラルではフィールド名、配列とスライスリテラルではインデックス、マップリテラルではキーとして解釈されます。マップリテラルの場合、すべての要素にキーが必要です。同じフィールド名または定数キー値を持つ複数の要素を指定するとエラーになります。非定数のマップキーについては、評価順序のセクションを参照してください。
構造体リテラルには以下のルールが適用されます:
- キーは構造体型で宣言されたフィールド名でなければなりません。
- キーを含まない要素リストは、フィールドが宣言されている順序で各構造体フィールドの要素をリストしなければなりません。
- いずれかの要素にキーがある場合、すべての要素にキーが必要です。
- キーを含む要素リストは、各構造体フィールドに要素を持つ必要はありません。省略されたフィールドはそのフィールドのゼロ値になります。
- リテラルは要素リストを省略することができます。そのようなリテラルはその型のゼロ値として評価されます。
- 異なるパッケージに属する構造体の非エクスポートフィールドに要素を指定するとエラーになります。
以下の宣言があるとします:
type Point3D struct { x, y, z float64 }
type Line struct { p, q Point3D }
次のように書くことができます:
origin := Point3D{} // Point3Dのゼロ値
line := Line{origin, Point3D{y: -4, z: 12.3}} // line.q.xのゼロ値
配列とスライスリテラルには以下のルールが適用されます:
- 各要素には、配列内の位置を示す関連する整数インデックスがあります。
- キーを持つ要素はそのキーをインデックスとして使用します。キーは
int
型の値で表現可能な非負の定数でなければなりません。型付けされている場合は整数型でなければなりません。 - キーを持たない要素は、前の要素のインデックスに1を加えたものを使用します。最初の要素にキーがない場合、そのインデックスは0です。
複合リテラルのアドレスを取得すると、そのリテラルの値で初期化された一意の変数へのポインタが生成されます。
var pointer *Point3D = &Point3D{y: 1000}
スライスまたはマップ型のゼロ値は、同じ型の初期化されたが空の値と同じではないことに注意してください。したがって、空のスライスやマップの複合リテラルのアドレスを取得することは、newで新しいスライスやマップ値を割り当てるのと同じ効果はありません。
p1 := &[]int{} // p1は初期化された空のスライスを指し、値は[]int{}で長さは0
p2 := new([]int) // p2は初期化されていないスライスを指し、値はnilで長さは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}} // [...]Point{Point{1.5, -3.5}, Point{0, 0}}と同じ
[][]int{{1, 2, 3}, {4, 5}} // [][]int{[]int{1, 2, 3}, []int{4, 5}}と同じ
[][]Point{{{0, 1}, {1, 2}}} // [][]Point{[]Point{Point{0, 1}, Point{1, 2}}}と同じ
map[string]Point{"orig": {0, 0}} // map[string]Point{"orig": Point{0, 0}}と同じ
map[Point]string{{0, 0}: "orig"} // map[Point]string{Point{0, 0}: "orig"}と同じ
type PPoint *Point
[2]*Point{{1.5, -3.5}, {}} // [2]*Point{&Point{1.5, -3.5}, &Point{}}と同じ
[2]PPoint{{1.5, -3.5}, {}} // [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]) { … }
有効な配列、スライス、およびマップリテラルの例:
// 素数のリスト
primes := []int{2, 3, 5, 7, 9, 2147483647}
// vowels[ch]はchが母音の場合trueになる
vowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}
// 配列[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}
// 平均律の周波数(Hz)(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.f
は値x
(または場合によっては*x
、下記参照)のフィールドまたはメソッドf
を示します。識別子f
は(フィールドまたはメソッドの)セレクタと呼ばれ、空白識別子であってはなりません。セレクタ式の型はf
の型です。x
がパッケージ名の場合は、修飾識別子のセクションを参照してください。
セレクタf
は型T
のフィールドまたはメソッドf
を示すか、T
の入れ子の埋め込みフィールドのフィールドまたはメソッドf
を参照することができます。f
に到達するために通過する埋め込みフィールドの数は、T
の深さと呼ばれます。T
で宣言されたフィールドまたはメソッドf
の深さはゼロです。T
の埋め込みフィールドA
で宣言されたフィールドまたはメソッドf
の深さは、A
におけるf
の深さに1を加えたものです。
セレクタには以下のルールが適用されます:
ポインタまたはインターフェース型ではない型
T
または*T
の値x
に対して、x.f
はT
の最も浅い深さにあるフィールドまたはメソッドf
を示します。最も浅い深さに一意的なf
が存在しない場合、セレクタ式は不正です。インターフェース型
I
の値x
に対して、x.f
はx
の動的な値の名前f
を持つ実際のメソッドを示します。I
のメソッドセットに名前f
のメソッドがない場合、セレクタ式は不正です。例外として、
x
の型が定義されたポインタ型であり、(*x).f
がフィールド(ただしメソッドではない)を示す有効なセレクタ式である場合、x.f
は(*x).f
の省略形です。他のすべての場合において、
x.f
は不正です。x
がポインタ型で値がnil
であり、x.f
が構造体フィールドを示す場合、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 // t.T0 != nilの場合
var p *T2 // p != nilかつ(*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は有効なフィールドセレクタ
p.M0() // ((*p).T0).M0() M0は*T0レシーバーを期待する
p.M1() // ((*p).T1).M1() M1はT1レシーバーを期待する
p.M2() // p.M2() M2は*T2レシーバーを期待する
t.M2() // (&t).M2() M2は*T2レシーバーを期待する、呼び出しのセクションを参照
しかし、以下は無効です:
q.M0() // (*q).M0は有効だがフィールドセレクタではない
メソッド式 #
M
が型T
のメソッドセットにある場合、T.M
は通常の関数として呼び出し可能な関数であり、M
と同じ引数を持ちますが、メソッドのレシーバーである追加の引数が先頭に付きます。
MethodExpr = ReceiverType "." MethodName .
ReceiverType = Type .
2つのメソッドを持つ構造体型T
を考えてみましょう。T
型のレシーバーを持つMv
と、*T
型のレシーバーを持つMp
です。
type T struct {
a int
}
func (tv T) Mv(a int) int { return 0 } // 値レシーバー
func (tp *T) Mp(f float32) float32 { return 1 } // ポインタレシーバー
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
が与えられた場合、f
はt.f(7)
ではなくf(t, 7)
として呼び出されます。レシーバーをバインドする関数を構築するには、関数リテラルまたはメソッド値を使用してください。
インターフェース型のメソッドから関数値を導出することは合法です。結果として得られる関数は、そのインターフェース型の明示的なレシーバーを取ります。
メソッド値 #
式x
が静的型T
を持ち、M
が型T
のメソッドセットにある場合、x.M
はメソッド値と呼ばれます。メソッド値x.M
は、x.M
のメソッド呼び出しと同じ引数で呼び出し可能な関数値です。式x
はメソッド値の評価中に評価され保存されます。保存されたコピーは、後で実行される可能性のある呼び出しでレシーバーとして使用されます。
type S struct { *T }
type T int
func (t T) M() { print(t) }
t := new(T)
s := S{T: t}
f := t.M // レシーバー*tが評価されfに格納される
g := s.M // レシーバー*(s.T)が評価されgに格納される
*t = 42 // fとgに格納されたレシーバーには影響しない
型T
はインターフェース型または非インターフェース型です。
上記のメソッド式の議論と同様に、2つのメソッドを持つ構造体型T
を考えてみましょう。T
型のレシーバーを持つMv
と、*T
型のレシーバーを持つMp
です。
type T struct {
a int
}
func (tv T) Mv(a int) int { return 0 } // 値レシーバー
func (tp *T) Mp(f float32) float32 { return 1 } // ポインタレシーバー
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) // t.Mv(7)のような
f := pt.Mp; f(7) // pt.Mp(7)のような
f := pt.Mv; f(7) // (*pt).Mv(7)のような
f := t.Mp; f(7) // (&t).Mp(7)のような
f := makeT().Mp // 無効:makeT()の結果はアドレス指定不可能
上記の例では非インターフェース型を使用していますが、インターフェース型の値からメソッド値を作成することも合法です。
var i interface { M(int) } = myVal
f := i.M; f(7) // i.M(7)のような
インデックス式 #
以下の形式の主式
a[x]
は配列、配列へのポインタ、スライス、文字列またはマップ a
の x
によってインデックス付けされた要素を表します。
値 x
は、それぞれ インデックス または マップキー と呼ばれます。
以下のルールが適用されます:
a
がマップでも型パラメータでもない場合:
- インデックス
x
は型付けされていない定数であるか、その型が整数または整数型のみを含む型集合を持つ型パラメータでなければなりません - 定数インデックスは非負であり、
int
型の値として表現可能でなければなりません - 型付けされていない定数インデックスには
int
型が与えられます - インデックス
x
は0 <= x < len(a)
の場合に 範囲内 であり、それ以外の場合は 範囲外 です
a
が配列型 A
の場合:
a
が配列型へのポインタの場合:
a[x]
は(*a)[x]
の省略形です
a
がスライス型 S
の場合:
x
が実行時に範囲外の場合、実行時パニックが発生しますa[x]
はインデックスx
のスライス要素であり、a[x]
の型はS
の要素型です
a
が文字列型の場合:
- 文字列
a
も定数である場合、定数インデックスは範囲内でなければなりません x
が実行時に範囲外の場合、実行時パニックが発生しますa[x]
はインデックスx
の非定数バイト値であり、a[x]
の型はbyte
ですa[x]
に代入することはできません
a
がマップ型 M
の場合:
x
の型はM
のキー型に代入可能でなければなりません- マップにキー
x
のエントリが含まれる場合、a[x]
はキーx
を持つマップ要素であり、a[x]
の型はM
の要素型です - マップが
nil
であるか、そのようなエントリを含まない場合、a[x]
はM
の要素型のゼロ値です
a
が型パラメータ型 P
の場合:
- インデックス式
a[x]
はP
の型集合内のすべての型の値に対して有効でなければなりません P
の型集合内のすべての型の要素型は同一でなければなりません。この文脈では、文字列型の要素型はbyte
ですP
の型集合にマップ型がある場合、その型集合内のすべての型はマップ型でなければならず、各キー型はすべて同一でなければなりませんa[x]
はP
がインスタンス化された型引数のインデックスx
での配列、スライス、または文字列要素、あるいはキーx
を持つマップ要素であり、a[x]
の型は(同一の)要素型の型ですP
の型集合に文字列型が含まれる場合、a[x]
に代入することはできません
それ以外の場合、a[x]
は不正です。
型 map[K]V
のマップ a
に対するインデックス式が、以下の特殊な形式の代入文または初期化で使用される場合:
v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]
追加の型付けされていないブール値が生成されます。ok
の値は、キー x
がマップに存在する場合は true
、それ以外の場合は false
です。
nil
マップの要素への代入は実行時パニックを引き起こします。
スライス式 #
スライス式は文字列、配列、配列へのポインタ、またはスライスのオペランドから部分文字列またはスライスを構築します。 2つの変種があります:下限と上限を指定する単純な形式と、容量の制限も指定する完全な形式です。
オペランド型が型パラメータである場合、文字列型を含む型集合でない限り、
型集合内のすべての型は同じ基底型を持つ必要があり、スライス式はその型のオペランドに対して有効でなければなりません。
型集合に文字列型が含まれる場合、基底型が []byte
のバイトスライスも含むことがあります。
この場合、スライス式は string
型のオペランドに対して有効でなければなりません。
単純なスライス式 #
文字列、配列、配列へのポインタ、またはスライス a
に対して、主式
a[low : high]
は部分文字列またはスライスを構築します。
インデックス low
と high
はオペランド 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:] // a[2 : len(a)]と同じ
a[:3] // a[0 : 3]と同じ
a[:] // 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] // s1の基底配列は配列a; &s1[2] == &a[5]
s2 := s1[1:4] // s2の基底配列はs1の基底配列である配列a; &s2[1] == &a[5]
s2[1] = 42 // s2[1] == s1[2] == a[5] == 42; すべて同じ基底配列要素を参照
var s []int
s3 := s[:0] // s3 == nil
完全なスライス式 #
配列、配列へのポインタ、またはスライス a
(ただし文字列ではない)に対して、主式
a[low : high : max]
は同じ型、同じ長さ、そして単純なスライス式 a[low : high]
と同じ要素を持つスライスを構築します。
さらに、結果のスライスの容量を max - low
に設定することでコントロールします。
最初のインデックスのみ省略可能で、デフォルトは0です。
配列 a
をスライスした後
a := [5]int{1, 2, 3, 4, 5}
t := a[1:3:5]
スライス t
は型 []int
、長さ2、容量4を持ち、要素は以下の通りです
t[0] == 2
t[1] == 3
単純なスライス式と同様に、a
が配列へのポインタである場合、
a[low : high : max]
は (*a)[low : high : max]
の省略形です。
スライス操作のオペランドが配列である場合、それはアドレス指定可能でなければなりません。
インデックスは 0 <= low <= high <= max <= cap(a)
の場合に 範囲内 であり、
それ以外の場合は 範囲外 です。
定数インデックスは非負であり、
int
型の値として表現可能でなければなりません。配列の場合、定数インデックスも範囲内でなければなりません。
複数のインデックスが定数である場合、存在する定数は互いに相対的に範囲内でなければなりません。
インデックスが実行時に範囲外である場合、実行時パニックが発生します。
型アサーション #
インターフェース型の式 x
(ただし型パラメータではない)と型 T
に対して、主式
x.(T)
は x
が nil
でなく、x
に格納された値が型 T
であることをアサートします。
表記 x.(T)
は 型アサーション と呼ばれます。
より正確には、T
がインターフェース型でない場合、x.(T)
は x
の動的型が型 T
と同一であることをアサートします。
この場合、T
は x
の(インターフェース)型を実装しなければなりません。
そうでなければ、x
が型 T
の値を格納することは不可能なため、型アサーションは無効です。
T
がインターフェース型である場合、x.(T)
は x
の動的型がインターフェース T
を実装していることをアサートします。
型アサーションが成立する場合、式の値は x
に格納された値であり、その型は T
です。
型アサーションが偽である場合、実行時パニックが発生します。
言い換えれば、x
の動的型が実行時にのみ知られるにもかかわらず、正しいプログラムでは x.(T)
の型は T
であることが分かっています。
var x interface{} = 7 // xは動的型intと値7を持つ
i := x.(int) // iは型intと値7を持つ
type I interface { m() }
func f(y I) {
s := y.(string) // 不正:stringはI(メソッドmがない)を実装していない
r := y.(io.Reader) // rは型io.Readerを持ち、yの動的型はIとio.Readerの両方を実装しなければならない
…
}
以下の特殊な形式の代入文または初期化で使用される型アサーション
v, ok = x.(T)
v, ok := x.(T)
var v, ok = x.(T)
var v, ok interface{} = x.(T) // vとokの動的型はそれぞれTとbool
は追加の型付けされていないブール値を生成します。ok
の値は、アサーションが成立する場合に true
です。
そうでない場合は false
であり、v
の値は型 T
のゼロ値です。
この場合、実行時パニックは発生しません。
関数呼び出し #
関数型 F
の式 f
に対して、
f(a1, a2, … an)
は引数 a1, a2, … an
で f
を呼び出します。
1つの特殊なケースを除いて、引数は単一値の式であり、F
のパラメータ型に代入可能でなければならず、関数が呼び出される前に評価されます。
式の型は F
の結果型です。
メソッド呼び出しも同様ですが、メソッド自体はメソッドのレシーバー型の値に対するセレクタとして指定されます。
math.Atan2(x, y) // 関数呼び出し
var pt *Point
pt.Scale(3.5) // レシーバーptを持つメソッド呼び出し
f
がジェネリック関数を示す場合、呼び出されるか関数値として使用される前にインスタンス化されなければなりません。
f
の型が型パラメータである場合、その型集合内のすべての型は同じ基底型を持つ必要があり、それは関数型でなければならず、関数呼び出しはその型に対して有効でなければなりません。
関数呼び出しでは、関数値と引数は通常の順序で評価されます。 評価後、関数の変数(パラメータと結果を含む)のための新しいストレージが割り当てられます。 次に、呼び出しの引数が関数に 渡され ます。これは、対応する関数パラメータに代入されることを意味し、呼び出された関数の実行が開始されます。 関数の戻りパラメータは、関数が戻るときに呼び出し元に渡されます。
nil
関数値の呼び出しは実行時パニックを引き起こします。
特殊なケースとして、関数またはメソッド g
の戻り値が、別の関数またはメソッド f
のパラメータと数が等しく個別に代入可能である場合、呼び出し f(g(parameters_of_g))
は g
の戻り値を順番に f
のパラメータに渡した後に f
を呼び出します。
f
の呼び出しには g
の呼び出し以外のパラメータを含めることはできず、g
は少なくとも1つの戻り値を持つ必要があります。
f
に最終的な ...
パラメータがある場合、通常のパラメータの割り当て後に残る g
の戻り値が割り当てられます。
func Split(s string, pos int) (string, string) {
return s[0:pos], s[pos:]
}
func Join(s, t string) string {
return s + t
}
if Join(Split(value, len(value)/2)) != value {
log.Panic("test fails")
}
メソッド呼び出し x.m()
は、(型の)x
のメソッドセットに m
が含まれ、引数リストが m
のパラメータリストに代入可能である場合に有効です。
x
がアドレス指定可能であり、&x
のメソッドセットに m
が含まれる場合、x.m()
は (&x).m()
の省略形です:
var p Point
p.Scale(3.5)
メソッド型とメソッドリテラルは存在しません。
...
パラメータへの引数の渡し方 #
f
が最終パラメータ p
が型 ...T
である可変引数である場合、f
内では p
の型は型 []T
と同等です。
f
が p
に対する実際の引数なしで呼び出された場合、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つのステップで進行します:
各型引数はジェネリック宣言の対応する型パラメータに代入されます。 この代入は型パラメータリスト自体とそのリスト内の任意の型を含め、関数または型宣言全体で行われます。
代入後、各型引数は対応する型パラメータの制約(必要に応じてインスタンス化される)を満たさなければなりません。 そうでなければインスタンス化は失敗します。
型をインスタンス化すると、新しい非ジェネリックな名前付き型が生成されます。 関数をインスタンス化すると、新しい非ジェネリックな関数が生成されます。
型パラメータリスト 型引数 代入後
[P any] int intはanyを満たす
[S ~[]E, E any] []int, int []intは~[]intを満たし、intはanyを満たす
[P io.Writer] string 不正:stringはio.Writerを満たさない
[P comparable] any anyはcomparableを満たす(ただし実装していない)
ジェネリック関数を使用する場合、型引数は明示的に提供されるか、関数が使用されるコンテキストから部分的または完全に推論される場合があります。 推論できる場合、関数が次のような場合には型引数リストを完全に省略できます:
その他のすべての場合、(場合によっては部分的な)型引数リストが存在しなければなりません。 型引数リストが存在しないか部分的である場合、欠けているすべての型引数は関数が使用されるコンテキストから推論可能でなければなりません。
// sumは引数の合計(文字列の場合は連結)を返します。
func sum[T ~int | ~float64 | ~string](x... T) T { … }
x := sum // 不正:xの型は不明
intSum := sum[int] // intSumはfunc(x... int) int型を持つ
a := intSum(2, 3) // aは型intの値5を持つ
b := sum[float64](2.0, 3) // bは型float64の値5.0を持つ
c := sum(b, -1) // cは型float64の値4.0を持つ
type sumFunc func(x... string) string
var f sumFunc = sum // var f sumFunc = sum[string]と同じ
f = sum // f = sum[string]と同じ
部分的な型引数リストは空にできません。少なくとも最初の引数が存在しなければなりません。 リストは型引数の完全なリストのプレフィックスであり、残りの引数は推論されます。 大まかに言えば、型引数は「右から左へ」省略できます。
func apply[S ~[]E, E any](s S, f func(E) E) S { … }
f0 := apply[] // 不正:型引数リストを空にすることはできない
f1 := apply[[]int] // Sの型引数が明示的に提供され、Eの型引数は推論される
f2 := apply[[]string, string] // 両方の型引数が明示的に提供される
var bytes []byte
r := apply(bytes, func(byte) byte { … }) // 両方の型引数が関数引数から推論される
ジェネリック型の場合、すべての型引数は常に明示的に提供されなければなりません。
型推論 #
ジェネリック関数の使用では、関数が使用されるコンテキスト(関数の型パラメータの制約を含む)から推論できる場合、一部または全部の型引数を省略できます。 型推論は、欠けている型引数を推論し、推論された型引数でインスタンス化が成功する場合に成功します。 そうでなければ、型推論は失敗し、プログラムは無効になります。
型推論は、型のペア間の型関係を推論に使用します。 例えば、関数引数はそれぞれの関数パラメータに代入可能でなければなりません。これにより、引数の型とパラメータの型の間に関係が確立されます。 これらの二つの型のいずれかに型パラメータが含まれる場合、型推論は代入可能関係が満たされるように型パラメータを置き換える型引数を探します。 同様に、型推論は型引数がそれぞれの型パラメータの制約を満たさなければならないという事実を使用します。
一致する型のこのようなペアは、一つまたは複数の型パラメータを含む型方程式に対応し、一つまたは場合によっては複数のジェネリック関数からのものです。 欠けている型引数を推論するとは、それぞれの型パラメータに対する型方程式の結果セットを解くことを意味します。
例えば、以下の場合を考えます:
// dedupは引数スライスの重複エントリを削除したコピーを返します。
func dedup[S ~[]E, E comparable](S) S { … }
type Slice []int
var s Slice
s = dedup(s) // 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(a0, a1, …)
において、f
または関数引数ai
がジェネリック関数である場合: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
は型方程式Pk ≡C Ck
を生成します。
型推論は、型付けされていない定数を考慮する前に、型付けされたオペランドから得られる型情報を優先します。 したがって、推論は2つのフェーズで進行します:
型方程式は型統一を使用して束縛された型パラメータについて解かれます。統一が失敗した場合、型推論は失敗します。
まだ型引数が推論されていない各束縛型パラメータ
Pk
について、同じ型パラメータを持つ一つ以上のペア(cj, 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
の型集合内のすべての型が同じ基底型U
を持ち、P
が既知の型引数A
を持つ場合、U
とA
は緩く統一される必要があります。同様に、
C
の型集合内のすべての型が同じ要素型と非競合チャネル方向を持つチャネル型であり、P
が既知の型引数A
を持つ場合、C
の型集合内の最も制限的なチャネル型とA
は緩く統一される必要があります。P
が既知の型引数を持たず、C
に基底(チルダ)型ではない正確に1つの型項T
が含まれる場合、統一はマップにP ➞ T
というマッピングを追加します。C
が上記のような型U
を持たず、P
が既知の型引数A
を持つ場合、A
はC
のすべてのメソッド(もしあれば)を持ち、対応するメソッド型は厳密に統一される必要があります。
型制約からの型方程式を解く際、一つの方程式を解くことで追加の型引数が推論され、それによってそれらの型引数に依存する他の方程式を解くことが可能になる場合があります。 型推論は新しい型引数が推論される限り型統一を繰り返します。
演算子 #
演算子はオペランドを組み合わせて式を形成します。
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 = "+" | "-" | "!" | "^" | "*" | "&" | "<-" .
比較演算子については別の箇所で説明します。 他の二項演算子では、シフト演算や型付けされていない定数を含む演算でない限り、オペランドの型は同一でなければなりません。 定数のみを含む演算については、定数式のセクションを参照してください。
シフト演算を除き、一方のオペランドが型付けされていない定数であり、もう一方がそうでない場合、定数は暗黙的に他方のオペランドの型に変換されます。
シフト式の右オペランドは整数型でなければならず、あるいはuint
型の値として表現可能な型付けされていない定数でなければなりません [Go 1.13]。
非定数シフト式の左オペランドが型付けされていない定数である場合、シフト式が左オペランドだけに置き換えられた場合に想定される型に最初に暗黙的に変換されます。
var a [1024]byte
var s uint = 33
// 以下の例の結果は64ビット整数に対するものです
var i = 1<<s // 1はint型
var j int32 = 1<<s // 1はint32型; j == 0
var k = uint64(1<<s) // 1はuint64型; k == 1<<33
var m int = 1.0<<s // 1.0はint型; m == 1<<33
var n = 1.0<<s == j // 1.0はint32型; n == true
var o = 1<<s == 2<<s // 1と2はint型; o == false
var p = 1<<s == 1<<33 // 1はint型; p == true
var u = 1.0<<s // 不正: 1.0はfloat64型、シフトできない
var u1 = 1.0<<s != 0 // 不正: 1.0はfloat64型、シフトできない
var u2 = 1<<s != 1.0 // 不正: 1はfloat64型、シフトできない
var v1 float32 = 1<<s // 不正: 1はfloat32型、シフトできない
var v2 = string(1<<s) // 不正: 1は文字列に変換され、シフトできない
var w int64 = 1.0<<33 // 1.0<<33は定数シフト式; w == 1<<33
var x = a[1.0<<s] // パニック: 1.0はint型だが、1<<33は配列の境界を超える
var b = make([]byte, 1.0<<s) // 1.0はint型; len(b) == 1<<33
// 以下の例の結果は32ビット整数に対するもので、
// シフトはオーバーフローします
var mm int = 1.0<<s // 1.0はint型; mm == 0
var oo = 1<<s == 2<<s // 1と2はint型; oo == true
var pp = 1<<s == 1<<33 // 不正: 1はint型だが、1<<33はintをオーバーフロー
var xx = a[1.0<<s] // 1.0はint型; xx == a[0]
var bb = make([]byte, 1.0<<s) // 1.0はint型; len(bb) == 0
演算子の優先順位 #
単項演算子は最も高い優先順位を持ちます。
++
と--
演算子は式ではなく文を形成するため、演算子の階層の外にあります。
その結果、文*p++
は(*p)++
と同じです。
二項演算子には5つの優先順位レベルがあります。
乗算演算子が最も強く結合し、続いて加算演算子、比較演算子、&&
(論理AND)、最後に||
(論理OR)の順です:
優先順位 演算子
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)
算術演算子 #
算術演算子は数値に適用され、最初のオペランドと同じ型の結果を生成します。四つの標準的な算術演算子(+
、-
、*
、/
)は整数、浮動小数点、複素数型に適用されます;+
は文字列にも適用されます。
ビット論理演算子とシフト演算子は整数にのみ適用されます。
+ 合計 整数、浮動小数点、複素数、文字列
- 差 整数、浮動小数点、複素数
* 積 整数、浮動小数点、複素数
/ 商 整数、浮動小数点、複素数
% 剰余 整数
& ビット単位AND 整数
| ビット単位OR 整数
^ ビット単位XOR 整数
&^ ビットクリア(AND NOT) 整数
<< 左シフト 整数 << 整数 >= 0
>> 右シフト 整数 >> 整数 >= 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
の精度で計算されます。
整数演算子 #
二つの整数値x
とy
について、整数の商q = x / y
と剰余r = x % y
は以下の関係を満たします:
x = q*y + r かつ |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
このルールの例外が一つあります。被除数x
がx
の整数型における最も負の値である場合、商q = x / -1
は2の補数の整数オーバーフローによりx
に等しくなります(そしてr = 0
):
x, q
int8 -128
int16 -32768
int32 -2147483648
int64 -9223372036854775808
除数が定数の場合、それはゼロであってはなりません。 実行時に除数がゼロの場合、実行時パニックが発生します。 被除数が非負で除数が2の定数べき乗である場合、除算は右シフトに置き換えられ、剰余の計算はビット単位のAND操作に置き換えられることがあります:
x x / 4 x % 4 x >> 2 x & 3
11 2 3 2 3
-11 -2 -3 -3 1
シフト演算子は、右オペランドで指定されたシフト数だけ左オペランドをシフトします。シフト数は非負でなければなりません。実行時にシフト数が負の場合、実行時パニックが発生します。
シフト演算子は、左オペランドが符号付き整数の場合は算術シフトを、符号なし整数の場合は論理シフトを実装します。
シフト数には上限がありません。シフトはシフト数n
に対して、左オペランドが1ずつn
回シフトされるかのように動作します。
その結果、x << 1
はx*2
と同じであり、x >> 1
はx/2
と同じですが、負の無限大に向かって切り捨てられます。
整数オペランドの場合、単項演算子+
、-
、および^
は次のように定義されます:
+x は 0 + x
-x 否定 は 0 - x
^x ビット単位の補数 は m ^ x ここで m = 符号なしxの場合は「すべてのビットが1に設定」
符号付きxの場合は -1
整数オーバーフロー #
符号なし整数値の場合、演算+
、-
、*
、および<<
は2nを法として計算されます。ここでnは符号なし整数の型のビット幅です。
簡単に言えば、これらの符号なし整数演算はオーバーフロー時に上位ビットを破棄し、プログラムは「ラップアラウンド」に依存することができます。
符号付き整数の場合、演算+
、-
、*
、/
、および<<
は合法的にオーバーフローする可能性があり、結果の値は符号付き整数の表現、演算、およびそのオペランドによって決定論的に定義されます。
オーバーフローは実行時パニックを引き起こしません。
コンパイラは、オーバーフローが発生しないという前提の下でコードを最適化することはできません。例えば、x < x + 1
が常に真であると仮定することはできません。
浮動小数点演算子 #
浮動小数点数と複素数の場合、+x
はx
と同じであり、-x
はx
の否定です。
ゼロによる浮動小数点または複素数の除算の結果はIEEE 754標準を超えて指定されておらず、実行時パニックが発生するかどうかは実装固有です。
実装は、複数の浮動小数点演算を単一の融合演算に組み合わせる場合があり、これは文をまたいで行われる可能性があり、命令を個別に実行して丸めることによって得られる値とは異なる結果を生成する場合があります。 明示的な浮動小数点型変換は、ターゲット型の精度に丸め、その丸めを破棄する融合を防ぎます。
例えば、一部のアーキテクチャでは「融合乗算加算」(FMA)命令が提供されており、中間結果x*y
を丸めずにx*y + z
を計算します。
これらの例は、Go実装がその命令をいつ使用できるかを示しています:
// rの計算にFMAが許可される、x*yが明示的に丸められないため:
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)
// rの計算にFMAが許可されない、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つのオペランドを比較し、型付けされていないブール値を生成します。
== 等しい
!= 等しくない
< より小さい
<= より小さいか等しい
> より大きい
>= より大きいか等しい
任意の比較において、最初のオペランドは2番目のオペランドの型に代入可能でなければならず、またはその逆も同様です。
等価演算子==
と!=
は比較可能な型のオペランドに適用されます。
順序付け演算子<
、<=
、>
、および>=
は順序付けられた型のオペランドに適用されます。
これらの用語と比較の結果は次のように定義されます:
ブール型は比較可能です。 2つのブール値は、両方が
true
または両方がfalse
の場合に等しくなります。整数型は比較可能で順序付けられています。 2つの整数値は通常の方法で比較されます。
浮動小数点型は比較可能で順序付けられています。 2つの浮動小数点値はIEEE 754標準で定義されているように比較されます。
複素数型は比較可能です。 2つの複素数値
u
とv
は、real(u) == real(v)
かつimag(u) == imag(v)
の場合に等しくなります。文字列型は比較可能で順序付けられています。 2つの文字列値はバイト単位で辞書的に比較されます。
ポインタ型は比較可能です。 2つのポインタ値は、同じ変数を指す場合、または両方の値が
nil
の場合に等しくなります。 異なるゼロサイズ変数へのポインタは等しいか等しくないかもしれません。チャネル型は比較可能です。 2つのチャネル値は、同じ
make
呼び出しによって作成された場合、または両方の値がnil
の場合に等しくなります。型パラメータではないインターフェース型は比較可能です。 2つのインターフェース値は、同一の動的型と等しい動的値を持つ場合、または両方の値が
nil
の場合に等しくなります。非インターフェース型
X
の値x
とインターフェース型T
の値t
は、型X
が比較可能で、X
がT
を実装している場合に比較できます。 これらは、t
の動的型がX
と同一であり、t
の動的値がx
に等しい場合に等しくなります。構造体型は、そのすべてのフィールド型が比較可能である場合に比較可能です。 2つの構造体値は、対応する非ブランクフィールド値が等しい場合に等しくなります。 フィールドはソース順に比較され、2つのフィールド値が異なるとすぐに比較が停止します(またはすべてのフィールドが比較された場合)。
配列型は、その配列要素型が比較可能である場合に比較可能です。 2つの配列値は、対応する要素値が等しい場合に等しくなります。 要素は昇順インデックス順に比較され、2つの要素値が異なるとすぐに比較が停止します(またはすべての要素が比較された場合)。
型パラメータは、厳密に比較可能(以下を参照)である場合に比較可能です。
2つの同一の動的型を持つインターフェース値の比較は、その型が比較可能でない場合に実行時パニックを引き起こします。この動作は、直接的なインターフェース値の比較だけでなく、インターフェース値の配列やインターフェース値のフィールドを持つ構造体を比較する場合にも適用されます。
スライス、マップ、および関数型は比較できません。
ただし、特別なケースとして、スライス、マップ、または関数値は、定義済み識別子nil
と比較できます。
ポインタ、チャネル、およびインターフェース値のnil
との比較も許可されており、上記の一般的なルールに従います。
const c = 3 < 4 // cは型付けされていないブール定数true
type MyBool bool
var x, y int
var (
// 比較の結果は型付けされていないブールです。
// 通常の代入ルールが適用されます。
b3 = x == y // b3はbool型
b4 bool = x == y // b4はbool型
b5 MyBool = x == y // b5はMyBool型
)
型は、比較可能でありインターフェース型でもインターフェース型で構成されていない場合、厳密に比較可能です。 具体的には:
ブール、数値、文字列、ポインタ、およびチャネル型は厳密に比較可能です。
構造体型は、そのすべてのフィールド型が厳密に比較可能である場合に厳密に比較可能です。
配列型は、その配列要素型が厳密に比較可能である場合に厳密に比較可能です。
型パラメータは、その型セット内のすべての型が厳密に比較可能である場合に厳密に比較可能です。
論理演算子 #
論理演算子はブール値に適用され、オペランドと同じ型の結果を生成します。 左オペランドが評価され、条件が必要とする場合は右オペランドも評価されます。
&& 条件付きAND p && q は "pならqそうでなければfalse"
|| 条件付きOR p || q は "pならtrueそうでなければq"
! NOT !p は "pでない"
アドレス演算子 #
型T
のオペランドx
に対して、アドレス操作&x
はx
への型*T
のポインタを生成します。
オペランドはアドレス可能でなければなりません。
つまり、変数、ポインタ逆参照、またはスライスのインデックス操作;アドレス可能な構造体オペランドのフィールドセレクタ;アドレス可能な配列のインデックス操作のいずれかである必要があります。
アドレス可能性要件の例外として、x
は(おそらく括弧で囲まれた)複合リテラルでもかまいません。
x
の評価が実行時パニックを引き起こす場合、&x
の評価も同様です。
ポインタ型*T
のオペランドx
に対して、ポインタ逆参照*x
はx
が指す型T
の変数を示します。
x
がnil
の場合、*x
を評価しようとすると実行時パニックが発生します。
&x
&a[f(2)]
&Point{2, 3}
*p
*pf(x)
var x *int = nil
*x // 実行時パニックを引き起こす
&*x // 実行時パニックを引き起こす
受信演算子 #
チャネル型のオペランドch
に対して、受信操作<-ch
の値はチャネルch
から受信した値です。
チャネルの方向は受信操作を許可する必要があり、受信操作の型はチャネルの要素型です。
式は値が利用可能になるまでブロックします。
nil
チャネルからの受信は永遠にブロックします。
閉じられたチャネルでの受信操作は、以前に送信された値がすべて受信された後に、要素型のゼロ値を生成して即座に進行できます。
v1 := <-ch
v2 = <-ch
f(<-ch)
<-strobe // クロックパルスを待ち、受信した値を破棄
オペランド型が型パラメータである場合、その型セット内のすべての型は受信操作を許可するチャネル型でなければならず、それらはすべて同じ要素型を持つ必要があります。これが受信操作の型となります。
特別な形式の代入文または初期化で使用される受信式
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) // *(Point(p)) と同じ
(*Point)(p) // p は *Point に変換される
<-chan int(c) // <-(chan int(c)) と同じ
(<-chan int)(c) // c は <-chan int に変換される
func()(x) // 関数シグネチャ func() x
(func())(x) // x は func() に変換される
(func() int)(x) // x は func() int に変換される
func() int(x) // x は func() int に変換される(明確)
定数値 x
は、x
が T
の値によって表現可能である場合、型 T
に変換できます。
特別なケースとして、整数定数 x
は非定数の x
と同じルールを使用して文字列型に明示的に変換できます。
型パラメータでない型に定数を変換すると、型付き定数が生成されます。
uint(iota) // uint型のiota値
float32(2.718281828) // float32型の2.718281828
complex128(1) // complex128型の1.0 + 0.0i
float32(0.49999999) // float32型の0.5
float64(-1e-1000) // float64型の0.0
string('x') // string型の"x"
string(0x266c) // string型の"♬"
myString("foo" + "bar") // myString型の"foobar"
string([]byte{'a'}) // 定数ではない: []byte{'a'}は定数ではない
(*int)(nil) // 定数ではない: nilは定数ではない、*intはブール値、数値、または文字列型ではない
int(1.2) // 不正: 1.2はintとして表現できない
string(65.0) // 不正: 65.0は整数定数ではない
定数を型パラメータに変換すると、その型の非定数値が生成されます。 この値は、型パラメータが実体化される型引数の値として表されます。 例えば、次の関数が与えられた場合:
func f[P ~float32|~float64]() {
… P(1.1) …
}
変換 P(1.1)
は型 P
の非定数値を生成し、値 1.1
は f
の型引数に応じて float32
または float64
として表されます。
従って、f
が float32
型で実体化される場合、式 P(1.1) + 1.2
の数値は、対応する非定数 float32
加算と同じ精度で計算されます。
非定数値 x
は、以下のいずれかの場合に型 T
に変換できます:
x
はT
に代入可能である。- 構造体タグを無視した場合(以下参照)、
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
に変換することもできます:
V
とT
の両方が型パラメータであり、V
の型セット内の各型の値がT
の型セット内の各型に変換できる。V
のみが型パラメータであり、V
の型セット内の各型の値がT
に変換できる。T
のみが型パラメータであり、x
がT
の型セット内の各型に変換できる。
変換目的で同一性を比較する際、構造体タグは無視されます:
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) // タグを無視すると、基底型は同一
数値型間または文字列型との間の(非定数)変換には、特定のルールが適用されます。
これらの変換は x
の表現を変更し、実行時コストが発生する場合があります。
他のすべての変換は型を変更するだけで、x
の表現は変更しません。
ポインタと整数の間を変換する言語的メカニズムはありません。 パッケージ unsafe は限定された状況でこの機能を実装しています。
数値型間の変換 #
非定数数値の変換には、以下のルールが適用されます:
整数型間の変換では、値が符号付き整数の場合、暗黙的な無限精度まで符号拡張されます。そうでない場合はゼロ拡張されます。 その後、結果型のサイズに収まるように切り捨てられます。 例えば、
v := uint16(0x10F0)
の場合、uint32(int8(v)) == 0xFFFFFFF0
となります。 変換は常に有効な値を生成し、オーバーフローの表示はありません。浮動小数点数を整数に変換する場合、小数部は破棄されます(ゼロ方向への切り捨て)。
整数または浮動小数点数を浮動小数点型に変換する場合、または複素数を別の複素数型に変換する場合、結果値は宛先型で指定された精度に丸められます。 例えば、
float32
型の変数x
の値は、IEEE 754 32ビット数値の精度を超えて追加精度で格納される場合がありますが、float32(x) はx
の値を32ビット精度に丸めた結果を表します。 同様に、x + 0.1
は32ビット以上の精度を使用する場合がありますが、float32(x + 0.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'}) // "🌍"
ルーンのスライスを文字列型に変換すると、個々のルーン値を文字列に変換して連結した文字列が生成されます。
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" == "🌎"
文字列型の値をバイトのスライス型に変換すると、文字列のバイトが連続する要素である非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'}
文字列型の値をルーンのスライス型に変換すると、文字列の個々のUnicodeコードポイントを含むスライスが生成されます。 結果のスライスの容量は実装固有であり、スライスの長さより大きい場合があります。
[]rune(myString("白鵬翔")) // []rune{0x767d, 0x9d6c, 0x7fd4} []rune("") // []rune{} runes("白鵬翔") // []rune{0x767d, 0x9d6c, 0x7fd4} []myRune("♫♬") // []myRune{0x266b, 0x266c} []myRune(myString("🌐")) // []myRune{0x1f310}
最後に、歴史的な理由から、整数値を文字列型に変換することができます。 この形式の変換は、指定された整数値を持つ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) // パニック: 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) // パニック: len([4]byte) > len(s)
var t []string
t0 := [0]string(t) // nil スライス t でも問題なし
t1 := (*[0]string)(t) // t1 == nil
t2 := (*[1]string)(t) // パニック: len([1]string) > len(t)
u := make([]byte, 0)
u0 := (*[0]byte)(u) // u0 != nil
定数式 #
定数式には定数オペランドのみを含めることができ、コンパイル時に評価されます。
型付けされていないブール値、数値、および文字列定数は、それぞれブール型、数値型、または文字列型のオペランドを使用することが合法な場所ならどこでもオペランドとして使用できます。
定数の比較は常に型付けされていないブール定数を生成します。 定数のシフト式の左オペランドが型付けされていない定数の場合、結果は整数定数になります。 それ以外の場合は、左オペランドと同じ型の定数になり、左オペランドは整数型でなければなりません。
型付けされていない定数に対する他の操作は、同じ種類の型付けされていない定数を生成します。 つまり、ブール値、整数、浮動小数点、複素数、または文字列定数です。 (シフト以外の)二項演算の型付けされていないオペランドが異なる種類の場合、結果は次のリストで後に現れるオペランドの種類となります:整数、ルーン、浮動小数点、複素数。 例えば、型付けされていない整数定数を型付けされていない複素数定数で割ると、型付けされていない複素数定数が生成されます。
const a = 2 + 3.0 // a == 5.0 (型付けされていない浮動小数点定数)
const b = 15 / 4 // b == 3 (型付けされていない整数定数)
const c = 15 / 4.0 // c == 3.75 (型付けされていない浮動小数点定数)
const Θ float64 = 3/2 // Θ == 1.0 (型 float64、3/2は整数除算)
const Π float64 = 3/2. // Π == 1.5 (型 float64、3/2.は浮動小数点除算)
const d = 1 << 3.0 // d == 8 (型付けされていない整数定数)
const e = 1.0 << 3 // e == 8 (型付けされていない整数定数)
const f = int32(1) << 33 // 不正 (定数 8589934592 が int32 をオーバーフロー)
const g = float64(2) >> 1 // 不正 (float64(2) は型付き浮動小数点定数)
const h = "foo" > "bar" // h == true (型付けされていないブール定数)
const j = true // j == true (型付けされていないブール定数)
const k = 'w' + 1 // k == 'x' (型付けされていないルーン定数)
const l = "hi" // l == "hi" (型付けされていない文字列定数)
const m = string(k) // m == "x" (型 string)
const Σ = 1 - 0.707i // (型付けされていない複素数定数)
const Δ = Σ + 2.0e-4 // (型付けされていない複素数定数)
const Φ = iota*1i - 1/1i // (型付けされていない複素数定数)
組み込み関数 complex
を型付けされていない整数、ルーン、または浮動小数点定数に適用すると、型付けされていない複素数定数が生成されます。
const ic = complex(0, c) // ic == 3.75i (型付けされていない複素数定数)
const iΘ = complex(0, Θ) // iΘ == 1i (型 complex128)
定数式は常に正確に評価されます。中間値および定数自体は、言語内の事前宣言型でサポートされているものよりも大幅に大きな精度を必要とする場合があります。以下は合法な宣言です:
const Huge = 1 << 100 // Huge == 1267650600228229401496703205376 (型付けされていない整数定数)
const Four int8 = Huge >> 98 // Four == 4 (型 int8)
定数の除算または剰余演算の除数はゼロであってはなりません:
3.14 / 0.0 // 不正: ゼロによる除算
型付き定数の値は、常に定数型の値によって正確に表現可能でなければなりません。以下の定数式は不正です:
uint(-1) // -1 は uint として表現できない
int(3.14) // 3.14 は int として表現できない
int64(Huge) // 1267650600228229401496703205376 は int64 として表現できない
Four * 300 // オペランド 300 は int8 (Four の型) として表現できない
Four * 100 // 積 400 は int8 (Four の型) として表現できない
単項ビット単位補数演算子 ^
で使用されるマスクは、非定数のルールに合わせています:マスクは符号なし定数の場合はすべて1、符号付きおよび型付けされていない定数の場合は-1です。
^1 // 型付けされていない整数定数、-2と等しい
uint8(^1) // 不正: uint8(-2)と同じ、-2はuint8として表現できない
^uint8(1) // 型付きuint8定数、0xFF ^ uint8(1) = uint8(0xFE)と同じ
int8(^1) // int8(-2)と同じ
^int8(1) // -1 ^ int8(1) = -2と同じ
実装制限:コンパイラは型付けされていない浮動小数点または複素数定数式を計算するときに丸めを使用する場合があります。 定数のセクションの実装制限を参照してください。 この丸めにより、無限精度で計算した場合に整数になるはずの浮動小数点定数式が整数コンテキストで無効になる場合があります(その逆も同様)。
評価順序 #
パッケージレベルでは、初期化依存関係が変数宣言内の個々の初期化式の評価順序を決定します。 それ以外の場合、式、代入、またはreturn文のオペランドを評価する際、すべての関数呼び出し、メソッド呼び出し、受信操作、および二項論理演算は語彙的に左から右の順序で評価されます。
例えば、(関数ローカルな)代入
y[f()], ok = g(z || h(), i()+x[j()], <-c), k()
では、関数呼び出しと通信は f()
、h()
(z
が false と評価される場合)、i()
、j()
、<-c
、g()
、k()
の順序で行われます。
ただし、これらのイベントの順序と x
の評価とインデックス付け、および y
と z
の評価の順序は、語彙的に必要な場合を除いて指定されていません。
例えば、g
はその引数が評価される前に呼び出すことはできません。
a := 1
f := func() int { a++; return a }
x := []int{a, f()} // xは[1, 2]または[2, 2]の可能性がある: aとf()の間の評価順序は指定されていない
m := map[int]int{a: 1, a: 2} // mは{2: 1}または{2: 2}の可能性がある: 2つのマップ代入間の評価順序は指定されていない
n := map[int]int{a: f()} // nは{2: 3}または{3: 3}の可能性がある: キーと値の間の評価順序は指定されていない
パッケージレベルでは、初期化依存関係は個々の初期化式に対する左から右のルールを上書きしますが、各式内のオペランドに対しては上書きしません:
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 }
// 関数uとvは他のすべての変数と関数から独立しています
関数呼び出しは u()
、sqr()
、v()
、f()
、v()
、g()
の順序で行われます。
単一の式内の浮動小数点演算は、演算子の結合性に従って評価されます。
明示的な括弧はデフォルトの結合性を上書きすることで評価に影響を与えます。
式 x + (y + z)
では、加算 y + z
が最初に実行され、その後 x
が加算されます。
ステートメント #
ステートメントは実行を制御します。
Statement = Declaration | LabeledStmt | SimpleStmt |
GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
DeferStmt .
SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .
終端ステートメント #
終端ステートメントはブロック内の通常の制御フローを中断します。以下のステートメントは終端ステートメントです:
組み込み関数
panic
の呼び出し。ステートメントリストが終端ステートメントで終わるブロック。
以下の条件を満たす“if"ステートメント:
- “else"ブランチが存在する、かつ
- 両方のブランチが終端ステートメントである。
以下の条件を満たす“for"ステートメント:
- その"for"ステートメントを参照する"break"ステートメントがない、かつ
- ループ条件が存在しない、かつ
- “for"ステートメントがrange句を使用していない。
以下の条件を満たす“switch"ステートメント:
- その"switch"ステートメントを参照する"break"ステートメントがない、かつ
- デフォルトケースがある、かつ
- 各ケース(デフォルトを含む)のステートメントリストが終端ステートメントで終わるか、ラベル付きの可能性がある“fallthrough"ステートメントで終わる。
以下の条件を満たす“select"ステートメント:
- その"select"ステートメントを参照する"break"ステートメントがない、かつ
- 各ケース(存在する場合はデフォルトを含む)のステートメントリストが終端ステートメントで終わる。
終端ステートメントにラベル付けされたラベル付きステートメント。
その他のすべてのステートメントは終端ではありません。
ステートメントリストが空でなく、その最後の非空ステートメントが終端ステートメントである場合、そのリストは終端ステートメントで終わります。
空ステートメント #
空ステートメントは何もしません。
EmptyStmt = .
ラベル付きステートメント #
ラベル付きステートメントはgoto
、break
またはcontinue
ステートメントの対象となる場合があります。
LabeledStmt = Label ":" Statement .
Label = identifier .
Error: log.Panic("error encountered")
式ステートメント #
特定の組み込み関数を除き、関数やメソッドの呼び出しおよび受信操作はステートメントコンテキストで現れることができます。このようなステートメントは括弧で囲むことができます。
ExpressionStmt = Expression .
以下の組み込み関数はステートメントコンテキストでは許可されていません:
append cap complex imag len make new real
unsafe.Add unsafe.Alignof unsafe.Offsetof unsafe.Sizeof unsafe.Slice unsafe.SliceData unsafe.String unsafe.StringData
h(x+y)
f.Close()
<-ch
(<-ch)
len("foo") // lenが組み込み関数の場合は不正
送信ステートメント #
送信ステートメントはチャネルに値を送信します。チャネル式はチャネル型である必要があり、チャネル方向は送信操作を許可する必要があり、送信される値の型はチャネルの要素型に代入可能である必要があります。
SendStmt = Channel "<-" Expression .
Channel = Expression .
通信が始まる前に、チャネルと値の式の両方が評価されます。通信は送信が進行できるまでブロックします。
バッファなしチャネルへの送信は、レシーバーが準備できている場合に進行できます。
バッファありチャネルへの送信は、バッファに空きがある場合に進行できます。
閉じたチャネルへの送信は実行時パニックを引き起こします。
nil
チャネルへの送信は永遠にブロックします。
ch <- 3 // 値3をチャネルchに送信
チャネル式の型が型パラメータの場合、そのタイプセット内のすべての型は送信操作を許可するチャネル型である必要があり、すべて同じ要素型を持ち、送信される値の型はその要素型に代入可能である必要があります。
インクリメント・デクリメントステートメント #
“++“と”–“ステートメントは、型なしの定数1
によってオペランドをインクリメントまたはデクリメントします。代入と同様に、オペランドはアドレス指定可能かマップインデックス式である必要があります。
IncDecStmt = Expression ( "++" | "--" ) .
以下の代入ステートメントは意味的に同等です:
インクリメント・デクリメントステートメント 代入
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 // k = <-chと同じ
代入操作 x
op=
y
(opはバイナリ算術演算子)は、x
=
x
op (y)
と同等ですが、x
は一度だけ評価されます。op=
の構造は単一のトークンです。代入操作では、左辺と右辺の式リストの両方が正確に1つの単一値式を含む必要があり、左辺の式はブランク識別子であってはなりません。
a[i] <<= 2
i &^= 1<<n
タプル代入は、多値操作の個々の要素を変数のリストに代入します。2つの形式があります。最初の形式では、右辺オペランドは関数呼び出し、チャネルやマップ操作、または型アサーションなどの単一の多値式です。左辺のオペランドの数は値の数と一致する必要があります。例えば、f
が2つの値を返す関数の場合:
x, y = f()
最初の値がx
に、2番目がy
に代入されます。
2番目の形式では、左辺のオペランドの数は右辺の式の数と等しくなければならず、それぞれが単一値でなければならず、右辺のn番目の式は左辺のn番目のオペランドに代入されます:
one, two, three = '一', '二', '三'
ブランク識別子は代入における右辺の値を無視する方法を提供します:
_ = x // xを評価するが無視する
x, _ = f() // f()を評価するが2番目の結果値を無視する
代入は2つのフェーズで進行します。 まず、左辺のインデックス式とポインタ間接参照(セレクタの暗黙のポインタ間接参照を含む)のオペランドと右辺の式がすべて通常の順序で評価されます。 次に、代入が左から右の順序で実行されます。
a, b = b, a // aとbを交換
x := []int{1, 2, 3}
i := 0
i, x[i] = 1, 2 // i = 1, x[0] = 2を設定
i = 0
x[i], i = 2, 1 // x[0] = 2, i = 1を設定
x[0], x[0] = 1, 2 // x[0] = 1を設定し、その後x[0] = 2(つまり最終的にx[0] == 2)
x[1], x[3] = 4, 5 // x[1] = 4を設定し、その後x[3] = 5でパニック。
type Point struct { x, y int }
var p *Point
x[2], p.x = 6, 7 // x[2] = 6を設定し、その後p.x = 7でパニック
i = 2
x = []int{3, 5, 7}
for i, x[i] = range x { // i, x[2] = 0, x[0]を設定
break
}
// このループの後、i == 0かつxは[]int{3, 5, 3}
代入において、各値は代入先のオペランドの型に代入可能でなければなりません。以下の特殊なケースがあります:
どんな型付きの値もブランク識別子に代入できます。
型なし定数がインターフェース型の変数またはブランク識別子に代入される場合、その定数は最初に暗黙的にそのデフォルト型に変換されます。
型なしブール値がインターフェース型の変数またはブランク識別子に代入される場合、それは最初に暗黙的に
bool
型に変換されます。
値が変数に代入されるとき、変数に格納されているデータのみが置き換えられます。値に参照が含まれている場合、代入は参照をコピーしますが、参照されるデータ(スライスの基底配列など)のコピーは作成しません。
var s1 = []int{1, 2, 3}
var s2 = s1 // s2はs1のスライス記述子を格納
s1 = s1[:1] // s1の長さは1だが、依然としてs2と基底配列を共有している
s2[0] = 42 // s2[0]を設定すると、s1[0]も変更される
fmt.Println(s1, s2) // [42] [42 2 3]と出力
var m1 = make(map[string]int)
var m2 = m1 // m2はm1のマップ記述子を格納
m1["foo"] = 42 // m1["foo"]を設定すると、m2["foo"]も変更される
fmt.Println(m2["foo"]) // 42と出力
if文 #
“if"文は、ブール式の値に応じて2つのブランチの条件付き実行を指定します。式がtrueと評価された場合、“if"ブランチが実行され、そうでなければ、存在する場合は"else"ブランチが実行されます。
IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .
if x > max {
x = max
}
式の前に単純なステートメントを置くことができ、これは式が評価される前に実行されます。
if x := f(); x < y {
return x
} else if x > z {
return z
} else {
return y
}
スイッチ文 #
「switch」文は複数の実行経路を提供します。式または型は「switch」内の「case」と比較され、実行するブランチが決定されます。
SwitchStmt = ExprSwitchStmt | TypeSwitchStmt .
スイッチ文には2つの形式があります:式スイッチと型スイッチです。 式スイッチでは、caseに含まれる式がswitch式の値と比較されます。 型スイッチでは、caseに含まれる型が特別に注釈されたswitch式の型と比較されます。 スイッチ式はスイッチ文内で正確に1回評価されます。
式スイッチ #
式スイッチでは、switch式が評価され、case式(定数である必要はありません)が左から右へ、上から下へと評価されます。switch式と等しい最初のcase式が、関連するcaseの文の実行をトリガーし、他のcaseはスキップされます。
いずれのcaseも一致せず、「default」caseがある場合は、その文が実行されます。
defaultケースは最大でも1つのみ許可され、「switch」文のどこにでも表示できます。
switch式が省略された場合は、論理値true
と同等になります。
ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
ExprCaseClause = ExprSwitchCase ":" StatementList .
ExprSwitchCase = "case" ExpressionList | "default" .
switch式が型付けされていない定数として評価される場合、最初に暗黙的にデフォルトの型に変換されます。
定義済みの型付けされていない値nil
はswitch式として使用できません。
switch式の型は比較可能である必要があります。
case式が型付けされていない場合、最初に暗黙的にswitch式の型に変換されます。
各(変換された可能性のある)case式x
とswitch式の値t
に対して、x == t
は有効な比較でなければなりません。
言い換えれば、switch式は、明示的な型なしで一時変数t
を宣言して初期化するのと同じように扱われます。各case式x
は、この値t
に対して等値性がテストされます。
caseまたはdefault句では、最後の空でない文が(ラベル付けされている可能性のある)fallthrough文となり、このclauseの終わりから次のclauseの最初の文へと制御が流れることを示すことができます。 それ以外の場合、制御は「switch」文の終わりに流れます。「fallthrough」文は、式スイッチの最後のclauseを除くすべてのclauseの最後の文として表示できます。
switch式の前に単純文を置くことができ、式の評価前に実行されます。
switch tag {
default: s3()
case 0, 1, 2, 3: s1()
case 4, 5, 6, 7: s2()
}
switch x := f(); { // switch式が省略されている場合は "true" を意味する
case x < 0: return -x
default: return x
}
switch {
case x < y: f1()
case x < z: f2()
case x == 4: f3()
}
実装制限:コンパイラは、同じ定数に評価される複数のcase式を禁止する場合があります。 例えば、現在のコンパイラはcase式内の重複する整数、浮動小数点、または文字列定数を禁止しています。
型スイッチ #
型スイッチは値ではなく型を比較します。それ以外は式スイッチに似ています。実際の型ではなくtype
キーワードを使用した型アサーションの形式の特別なswitch式によってマークされます:
switch x.(type) {
// cases
}
caseは実際の型T
を式x
の動的型と照合します。型アサーションと同様に、x
はインターフェース型である必要がありますが、型パラメータではありません。また、caseにリストされている各非インターフェース型T
はx
の型を実装している必要があります。
型スイッチのcaseにリストされる型はすべて異なる必要があります。
TypeSwitchStmt = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" .
TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
TypeCaseClause = TypeSwitchCase ":" StatementList .
TypeSwitchCase = "case" TypeList | "default" .
TypeSwitchGuardには短い変数宣言を含めることができます。 この形式が使用される場合、変数は各節の暗黙的なブロックのTypeSwitchCaseの末尾で宣言されます。 正確に1つの型をリストするcase句では、変数はその型を持ちます。それ以外の場合、変数はTypeSwitchGuard内の式の型を持ちます。
型の代わりに、caseは予め宣言された識別子nil
を使用することができます。
このcaseは、TypeSwitchGuard内の式がnil
インターフェース値である場合に選択されます。
nil
のcaseは最大でも1つしか存在できません。
型interface{}
の式x
があると仮定すると、次の型スイッチは:
switch i := x.(type) {
case nil:
printString("x is nil") // iの型はxの型(interface{})
case int:
printInt(i) // iの型はint
case float64:
printFloat64(i) // iの型はfloat64
case func(int) float64:
printFunction(i) // iの型はfunc(int) float64
case bool, string:
printString("type is bool or string") // iの型はxの型(interface{})
default:
printString("don't know the type") // iの型はxの型(interface{})
}
以下のように書き直すことができます:
v := x // xは正確に一度評価される
if v == nil {
i := v // iの型はxの型(interface{})
printString("x is nil")
} else if i, isInt := v.(int); isInt {
printInt(i) // iの型はint
} else if i, isFloat64 := v.(float64); isFloat64 {
printFloat64(i) // iの型はfloat64
} else if i, isFunc := v.(func(int) float64); isFunc {
printFunction(i) // iの型はfunc(int) float64
} else {
_, isBool := v.(bool)
_, isString := v.(string)
if isBool || isString {
i := v // iの型はxの型(interface{})
printString("type is bool or string")
} else {
i := v // iの型はxの型(interface{})
printString("don't know the type")
}
}
型パラメータまたはジェネリック型をcaseの型として使用できます。インスタンス化時にその型がスイッチ内の別のエントリと重複する場合、最初に一致するcaseが選択されます。
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
と同等です。
for a < b {
a *= 2
}
for
句付きのfor文 #
ForClause付きの「for」文も条件によって制御されますが、 追加して代入、インクリメントまたはデクリメント文などの初期化(init)文と後処理(post)文を指定できます。init文は短い変数宣言かもしれませんが、post文はそうではありません。
ForClause = [ InitStmt ] ";" [ Condition ] ";" [ PostStmt ] .
InitStmt = SimpleStmt .
PostStmt = SimpleStmt .
for i := 0; i < 10; i++ {
f(i)
}
空でない場合、init文は最初の繰り返しの条件を評価する前に一度だけ実行されます。
post文はブロックの各実行後に実行されます(そしてブロックが実行された場合のみ)。
ForClauseの任意の要素は空でもよいですが、条件のみが存在する場合を除き、セミコロンは必須です。
条件が省略された場合は、ブール値true
と同等です。
for cond { S() } は for ; cond ; { S() } と同じ
for { S() } は for true { S() } と同じ
各繰り返しは独自の宣言された変数(または複数の変数)を持ちます(Go 1.22)。 最初の繰り返しで使用される変数はinit文によって宣言されます。 それ以降の各繰り返しで使用される変数は、post文の実行前に暗黙的に宣言され、その時点での前の繰り返しの変数の値で初期化されます。
var prints []func()
for i := 0; i < 5; i++ {
prints = append(prints, func() { println(i) })
i++
}
for _, p := range prints {
p()
}
これは以下を出力します:
1
3
5
Go 1.22以前は、繰り返しは独自の別々の変数を持つ代わりに1セットの変数を共有していました。 その場合、上の例は以下を出力します:
6
6
6
range
句付きのfor文 #
「range」句付きの「for」文は、配列、スライス、文字列またはマップのすべてのエントリ、チャネルで受信した値、ゼロから上限までの整数値(Go 1.22)、またはイテレータ関数のyield関数に渡された値(Go 1.23)を繰り返し処理します。 各エントリに対して、存在する場合は対応する繰り返し変数に繰り返し値を割り当て、その後ブロックを実行します。
RangeClause = [ ExpressionList "=" | IdentifierList ":=" ] "range" Expression .
「range」句の右側の式は「range式」と呼ばれ、配列、配列へのポインタ、スライス、文字列、マップ、受信操作を許可するチャネル、整数、または特定のシグネチャを持つ関数(以下参照)のいずれかです。 代入と同様に、左側のオペランドが存在する場合はアドレス指定可能またはマップのインデックス式でなければなりません。それらは繰り返し変数を示します。 range式が関数の場合、繰り返し変数の最大数は関数のシグネチャによって異なります。 range式がチャネルまたは整数の場合、最大でも1つの繰り返し変数が許可されます。 それ以外の場合は最大で2つの繰り返し変数が存在できます。 最後の繰り返し変数がブランク識別子の場合、range句はその識別子のない同じ句と同等です。
range式x
は、ループを開始する前に評価されます。ただし、1つ以下の繰り返し変数が存在し、x
またはlen(x)
が定数である場合は例外です。この場合、range式は評価されません。
左側の関数呼び出しは繰り返しごとに1回評価されます。 各繰り返しについて、対応する繰り返し変数が存在する場合、繰り返し値は次のように生成されます:
Range式 第1の値 第2の値
配列またはスライス a [n]E, *[n]E, または []E インデックス i int a[i] E
文字列 s string型 インデックス i int 下記参照 rune
マップ m map[K]V キー k K m[k] V
チャネル c chan E, <-chan E 要素 e E
整数値 n 整数型、または型なし整数 値 i 下記参照
関数、0個の値 f func(func() bool)
関数、1個の値 f func(func(V) bool) 値 v V
関数、2個の値 f func(func(K, V) bool) キー k K v V
配列、配列へのポインタ、またはスライス値
a
の場合、インデックス繰り返し値は要素インデックス0から始まる増加順で生成されます。 最大で1つの繰り返し変数が存在する場合、rangeループは0からlen(a)-1
までの繰り返し値を生成し、配列またはスライス自体にはインデックスを付けません。nil
スライスの場合、繰り返しの数は0です。文字列値の場合、「range」句はバイトインデックス0から始まる文字列内のUnicodeコードポイントを繰り返し処理します。連続する繰り返しでは、インデックス値は文字列内の連続するUTF-8エンコードされたコードポイントの最初のバイトのインデックスとなり、
rune
型の2番目の値は対応するコードポイントの値となります。繰り返しが無効なUTF-8シーケンスに遭遇した場合、2番目の値は0xFFFD
(Unicodeの置換文字)となり、次の繰り返しでは文字列内の単一バイトが進みます。マップの繰り返し順序は指定されておらず、ある繰り返しから次の繰り返しまで同じであることは保証されていません。 まだ到達していないマップエントリが繰り返し中に削除された場合、対応する繰り返し値は生成されません。マップエントリが繰り返し中に作成された場合、そのエントリは繰り返し中に生成されるか、スキップされる可能性があります。この選択は作成された各エントリによって異なり、繰り返しごとに変わる可能性があります。 マップが
nil
の場合、繰り返しの数は0です。チャネルの場合、生成される繰り返し値は、チャネルがクローズされるまでにチャネルに送信された連続した値です。チャネルが
nil
の場合、range式は永遠にブロックします。整数値
n
の場合、n
が整数型または型なしの整数定数である場合、繰り返し値0からn-1
が増加順で生成されます。n
が整数型の場合、繰り返し値はその同じ型を持ちます。 それ以外の場合、n
の型は繰り返し変数に代入されるかのように決定されます。 具体的には: 繰り返し変数が既存のものである場合、繰り返し値の型は繰り返し変数の型であり、これは整数型でなければなりません。 それ以外の場合、繰り返し変数が「range」句によって宣言されるか存在しない場合、繰り返し値の型はn
のデフォルト型です。n
<= 0の場合、ループは繰り返しを実行しません。関数
f
の場合、繰り返しは引数として新しく合成されたyield
関数をf
に渡すことによって進行します。f
が戻る前にyield
が呼び出された場合、yield
への引数はループ本体を一度実行するための繰り返し値になります。 各連続したループ繰り返しの後、yield
はtrueを返し、ループを続行するために再び呼び出される可能性があります。 ループ本体が終了しない限り、「range」句はf
が戻るまで、各yield
呼び出しに対してこの方法で繰り返し値を生成し続けます。 ループ本体が終了した場合(例えばbreak
文による)、yield
はfalseを返し、再び呼び出されてはなりません。
繰り返し変数は、短い変数宣言の形式(:=
)を使用して「range」句によって宣言できます。
この場合、それらのスコープは「for」文のブロックであり、各繰り返しは独自の新しい変数を持ちます(Go 1.22)(ForClause付きの「for」文も参照)。
変数はそれぞれの繰り返し値の型を持ちます。
繰り返し変数が「range」句によって明示的に宣言されていない場合、それらは既存のものでなければなりません。 この場合、繰り返し値は代入文のように、それぞれの変数に割り当てられます。
var testdata *struct {
a *[7]int
}
for i, _ := range testdata.a {
// testdata.aは評価されない。len(testdata.a)は定数
// iは0から6までの範囲
f(i)
}
var a [10]string
for i, s := range a {
// iの型はint
// sの型はstring
// s == a[i]
g(i, s)
}
var key string
var val interface{} // mの要素型は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 == 繰り返しで最後に遭遇したマップキー
// val == map[key]
var ch chan Work = producer()
for w := range ch {
doWork(w)
}
// チャネルを空にする
for range ch {}
// f(0), f(1), ... f(9)を呼び出す
for i := range 10 {
// iの型はint(型なし定数10のデフォルト型)
f(i)
}
// 無効:256はuint8に代入できない
var u uint8
for u = range 256 {
}
// 無効:1e3は浮動小数点定数
for range 1e3 {
}
// fiboはフィボナッチ数列を生成する
fibo := func(yield func(x int) bool) {
f0, f1 := 0, 1
for yield(f0) {
f0, f1 = f1, f0+f1
}
}
// 1000未満のフィボナッチ数を出力:
for x := range fibo {
if x >= 1000 {
break
}
fmt.Printf("%d ", x)
}
// 出力: 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
// 再帰的ツリーデータ構造の繰り返しサポート
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)
}
// ツリーtを中間順で走査
var t Tree[string, int]
for k, v := range t.Walk {
// k, vを処理
}
range式の型が型パラメータである場合、その型セット内のすべての型は同じ基底型を持ち、range式はその型に対して有効でなければなりません。または、型セットにチャネル型が含まれる場合、それは同一の要素型を持つチャネル型のみを含み、すべてのチャネル型は受信操作を許可しなければなりません。
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は(括弧で囲まれている可能性のある)受信操作でなければなりません。defaultケースは最大で1つだけ存在でき、ケースリストのどこにでも配置できます。
「select」ステートメントの実行は、いくつかのステップで進行します:
ステートメント内のすべてのケースに対して、受信操作のチャネルオペランドと送信ステートメントのチャネルおよび右辺式は、「select」ステートメントに入った時点で、ソース順に一度だけ評価されます。結果は、受信元または送信先のチャネルのセットとそれに対応する送信値です。この評価における副作用は、どの通信操作が選択されるかに関係なく発生します。短い変数宣言または代入を伴うRecvStmtの左辺の式はまだ評価されていません。
1つ以上の通信が進行できる場合、進行可能な1つがランダムに選択されます。そうでなく、defaultケースがある場合は、そのケースが選択されます。defaultケースがない場合、「select」ステートメントは少なくとも1つの通信が進行できるようになるまでブロックします。
選択されたケースがdefaultケースでない限り、それぞれの通信操作が実行されます。
選択されたケースが短い変数宣言または代入を伴うRecvStmtの場合、左辺の式が評価され、受信した値が代入されます。
選択されたケースのステートメントリストが実行されます。
nil
チャネルでの通信は決して進行できないため、nil
チャネルのみでdefaultケースがない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): // i3, ok := <-c3 と同じ
if ok {
print("received ", i3, " from c3\n")
} else {
print("c3 is closed\n")
}
case a[f()] = <-c4:
// 次と同じ:
// case t := <-c4
// a[f()] = t
default:
print("no communication\n")
}
for { // ランダムなビット列をcに送信
select {
case c <- 0: // 注意:ステートメントなし、フォールスルーなし、ケースの折りたたみなし
case c <- 1:
}
}
select {} // 永遠にブロック
returnステートメント #
関数F
内の「return」ステートメントは、F
の実行を終了し、オプションで1つ以上の結果値を提供します。F
によって遅延された関数は、F
が呼び出し元に戻る前に実行されます。
ReturnStmt = "return" [ ExpressionList ] .
結果型のない関数では、「return」ステートメントは結果値を指定してはいけません。
func noResult() {
return
}
結果型を持つ関数から値を返す方法は3つあります:
- 「return」ステートメントで戻り値を明示的にリストできます。各式は単一値であり、関数の結果型の対応する要素に代入可能でなければなりません。
func simpleF() int {
return 2
}
func complexF1() (re float64, im float64) {
return -7.0, -4.0
}
- 「return」ステートメントの式リストは、複数の値を返す関数への単一の呼び出しであることがあります。その効果は、その関数から返される各値がそれぞれの値の型を持つ一時変数に代入され、その後これらの変数をリストする「return」ステートメントが続くかのようになります。この時点で前のケースのルールが適用されます。
func complexF2() (re float64, im float64) {
return complexF1()
}
- 関数の結果型がその結果パラメータに名前を指定している場合、式リストは空であってもかまいません。結果パラメータは通常のローカル変数として機能し、関数は必要に応じてそれらに値を代入することができます。「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」ステートメントでの空の式リストを禁止することがあります。
func f(n int) (res int, err error) {
if _, err := f(n-1); err != nil {
return // 無効なreturnステートメント:errは影になっている
}
return
}
breakステートメント #
「break」ステートメントは、同じ関数内の最も内側の「for」、「switch」、または「select」ステートメントの実行を終了します。
BreakStmt = "break" [ Label ] .
ラベルがある場合、それは囲む「for」、「switch」、または「select」ステートメントのものでなければならず、その実行が終了します。
OuterLoop:
for i = 0; i < n; i++ {
for j = 0; j < m; j++ {
switch a[i][j] {
case nil:
state = Error
break OuterLoop
case item:
state = Found
break OuterLoop
}
}
}
continueステートメント #
「continue」ステートメントは、制御をループブロックの終わりに進めることで、最も内側の「for」ループの次の反復を開始します。「for」ループは同じ関数内になければなりません。
ContinueStmt = "continue" [ Label ] .
ラベルがある場合、それは囲む「for」ステートメントのものでなければならず、その実行が進められます。
RowLoop:
for y, row := range rows {
for x, data := range row {
if data == endOfRow {
continue RowLoop
}
row[x] = data + bias(x, y)
}
}
gotoステートメント #
「goto」ステートメントは、同じ関数内の対応するラベルを持つステートメントに制御を転送します。
GotoStmt = "goto" Label .
goto Error
「goto」ステートメントの実行により、goto時点ですでにスコープ内にない変数がスコープに入ることがあってはなりません。例えば:
goto L // BAD
v := 3
L:
これは、ラベルL
へのジャンプがv
の作成をスキップするため、誤りです。
ブロック外の「goto」ステートメントは、そのブロック内のラベルにジャンプできません。例えば:
if n%2 == 1 {
goto L1
}
for n > 0 {
f()
n--
L1:
f()
n--
}
これは、ラベルL1
が「for」ステートメントのブロック内にあるのに対し、goto
はそうではないため、誤りです。
fallthroughステートメント #
「fallthrough」ステートメントは、式「switch」ステートメント内の次のcase句の最初のステートメントに制御を転送します。このステートメントは、そのようなclause内の最後の非空ステートメントとしてのみ使用できます。
FallthroughStmt = "fallthrough" .
deferステートメント #
「defer」ステートメントは、周囲の関数がreturnステートメントを実行した、関数本体の終わりに達した、または対応するゴルーチンがパニックしているために、周囲の関数が戻る瞬間まで実行が遅延される関数を呼び出します。
DeferStmt = "defer" Expression .
式は関数またはメソッド呼び出しでなければならず、括弧で囲むことはできません。組み込み関数の呼び出しは、式ステートメントと同様に制限されています。
「defer」ステートメントが実行されるたびに、関数値と呼び出しのパラメータは通常通り評価され、新たに保存されますが、実際の関数は呼び出されません。代わりに、遅延関数は周囲の関数が戻る直前に、遅延された逆順で呼び出されます。つまり、周囲の関数が明示的なreturnステートメントを通じて戻る場合、遅延関数はそのreturnステートメントによって結果パラメータが設定された後、しかし関数が呼び出し元に戻る前に実行されます。遅延関数値がnil
と評価される場合、「defer」ステートメントが実行されるときではなく、関数が呼び出されるときにパニックが発生します。
例えば、遅延関数が関数リテラルであり、周囲の関数がリテラル内でスコープにある名前付き結果パラメータを持つ場合、遅延関数は結果パラメータが返される前にアクセスして変更することができます。遅延関数が戻り値を持つ場合、関数が完了するとそれらは破棄されます。(パニックの処理に関するセクションも参照)
lock(l)
defer unlock(l) // ロック解除は周囲の関数が戻る前に発生
// 周囲の関数が戻る前に3 2 1 0を印刷
for i := 0; i <= 3; i++ {
defer fmt.Print(i)
}
// fは42を返す
func f() (result int) {
defer func() {
// resultはreturnステートメントで6に設定された後にアクセスされる
result *= 7
}()
return 6
}
組み込み関数 #
組み込み関数は事前に宣言されています。これらは他の関数と同様に呼び出されますが、いくつかの関数は最初の引数として式の代わりに型を受け取ります。
組み込み関数は標準のGo型を持たないため、関数呼び出し式でのみ使用可能で、関数値として使用することはできません。
スライスの追加とコピー #
組み込み関数 append
と copy
は一般的なスライス操作に役立ちます。両方の関数において、引数が参照するメモリが重複しているかどうかにかかわらず、結果は独立しています。
可変引数関数 append
はスライス s
(型 S
)に0個以上の値 x
を追加し、結果として得られるスライス(同じく型 S
)を返します。値 x
は型 ...E
のパラメータに渡されます。ここで E
は S
の要素型であり、それぞれのパラメータ渡しのルールが適用されます。特殊なケースとして、append
は最初の引数として型 []byte
に割り当て可能な値と、2番目の引数として文字列型の後に ...
を付けた値も受け付けます。この形式は文字列のバイトを追加します。
append(s S, x ...E) S // E は S の要素型
S
が型パラメータの場合、そのタイプセット内のすべての型は同じ基本スライス型 []E
を持つ必要があります。
s
の容量が追加の値を収めるのに十分でない場合、append
は既存のスライス要素と追加の値の両方を収める新しい、十分に大きな基底配列を割り当てます。それ以外の場合、append
は基底配列を再利用します。
s0 := []int{0, 0}
s1 := append(s0, 2) // 単一要素の追加 s1 は []int{0, 0, 2}
s2 := append(s1, 3, 5, 7) // 複数要素の追加 s2 は []int{0, 0, 2, 3, 5, 7}
s3 := append(s2, s0...) // スライスの追加 s3 は []int{0, 0, 2, 3, 5, 7, 0, 0}
s4 := append(s3[3:6], s3[2:]...) // 重複するスライスの追加 s4 は []int{3, 5, 7, 2, 3, 5, 7, 0, 0}
var t []interface{}
t = append(t, 42, 3.1415, "foo") // t は []interface{}{42, 3.1415, "foo"}
var b []byte
b = append(b, "bar"...) // 文字列内容の追加 b は []byte{'b', 'a', 'r' }
関数 copy
はソース src
からデスティネーション dst
へスライス要素をコピーし、コピーされた要素の数を返します。両方の引数は同一の要素型 E
を持ち、型 []E
のスライスに割り当て可能でなければなりません。コピーされる要素の数は len(src)
と len(dst)
の小さい方になります。特殊なケースとして、copy
はデスティネーション引数として型 []byte
に割り当て可能な値と、ソース引数として string
型の値も受け付けます。この形式は文字列からのバイトをバイトスライスにコピーします。
copy(dst, src []T) int
copy(dst []byte, src string) int
引数の1つまたは両方の型が型パラメータの場合、それぞれのタイプセット内のすべての型は同じ基本スライス型 []E
を持つ必要があります。
例:
var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
var s = make([]int, 6)
var b = make([]byte, 5)
n1 := copy(s, a[0:]) // n1 == 6, s は []int{0, 1, 2, 3, 4, 5}
n2 := copy(s, s[2:]) // n2 == 4, s は []int{2, 3, 4, 5, 4, 5}
n3 := copy(b, "Hello, World!") // n3 == 5, b は []byte("Hello")
クリア #
組み込み関数 clear
は マップ、スライス、または型パラメータ型の引数を取り、すべての要素を削除またはゼロにします [Go 1.21]。
Call 引数型 結果
clear(m) map[K]T すべてのエントリを削除し、空のマップにする (len(m) == 0)
clear(s) []T s の長さまですべての要素を T のゼロ値に設定する
clear(t) 型パラメータ 下記参照
clear
への引数の型が型パラメータである場合、そのタイプセット内のすべての型はマップまたはスライスでなければならず、clear
は実際の型引数に対応する操作を実行します。
マップまたはスライスが nil
の場合、clear
は何も行いません。
クローズ #
チャネル ch
に対して、組み込み関数 close(ch)
はそのチャネルにこれ以上値が送信されないことを記録します。ch
が受信専用チャネルの場合はエラーとなります。閉じられたチャネルへの送信や閉鎖は実行時パニックを引き起こします。nilチャネルの閉鎖も実行時パニックを引き起こします。close
を呼び出した後、そして以前に送信された値がすべて受信された後、受信操作はブロックせずにチャネルの型のゼロ値を返します。複数値の受信操作は、受信した値とチャネルが閉じられているかどうかの表示を共に返します。
close
への引数の型が型パラメータである場合、そのタイプセット内のすべての型は同じ要素型を持つチャネルでなければなりません。それらのチャネルのいずれかが受信専用チャネルである場合はエラーとなります。
複素数の操作 #
3つの関数が複素数の組み立てと分解を行います。組み込み関数 complex
は浮動小数点の実部と虚部から複素数値を構築し、real
と imag
は複素数値から実部と虚部を抽出します。
complex(realPart, imaginaryPart floatT) complexT
real(complexT) floatT
imag(complexT) floatT
引数と戻り値の型は対応しています。complex
では、2つの引数は同じ浮動小数点型でなければならず、戻り値の型は対応する浮動小数点成分を持つ複素数型です:float32
引数に対しては complex64
、float64
引数に対しては complex128
です。引数の1つが型なし定数として評価される場合、それは最初に暗黙的に他の引数の型に変換されます。両方の引数が型なし定数として評価される場合、それらは非複素数であるか、虚部がゼロでなければならず、関数の戻り値は型なし複素定数となります。
real
と imag
の場合、引数は複素数型でなければならず、戻り値の型は対応する浮動小数点型です:complex64
引数に対しては float32
、complex128
引数に対しては 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) // 型なし複素定数 1 - 1.4i
x := float32(math.Cos(math.Pi/2)) // float32
var c64 = complex(5, -x) // complex64
var s int = complex(1, 0) // 型なし複素定数 1 + 0i は int に変換可能
_ = complex(1, 2<<s) // 不正: 2 は浮動小数点型と見なされ、シフトできない
var rl = real(c64) // float32
var im = imag(a) // float64
const c = imag(b) // 型なし定数 -1.4
_ = imag(3 << s) // 不正: 3 は複素数型と見なされ、シフトできない
型パラメータ型の引数は許可されていません。
マップ要素の削除 #
組み込み関数 delete
はマップ m
からキー k
を持つ要素を削除します。値 k
は m
のキー型に割り当て可能でなければなりません。
delete(m, k) // マップ m から要素 m[k] を削除
m
の型が型パラメータである場合、そのタイプセット内のすべての型はマップでなければならず、それらはすべて同一のキー型を持つ必要があります。
マップ m
が nil
であるか、要素 m[k]
が存在しない場合、delete
は何も行いません。
長さと容量 #
組み込み関数 len
と cap
は様々な型の引数を取り、int
型の結果を返します。実装は結果が常に int
に収まることを保証します。
呼び出し 引数型 結果
len(s) string型 文字列の長さ(バイト単位)
[n]T, *[n]T 配列の長さ(== n)
[]T スライスの長さ
map[K]T マップの長さ(定義されたキーの数)
chan T チャネルバッファにキューイングされた要素の数
型パラメータ 下記参照
cap(s) [n]T, *[n]T 配列の長さ(== n)
[]T スライスの容量
chan T チャネルバッファの容量
型パラメータ 下記参照
引数型が型パラメータ P
である場合、呼び出し len(e)
(または cap(e)
)は P
のタイプセット内の各型に対して有効でなければなりません。結果は、P
がインスタンス化された型引数に対応する型を持つ引数の長さ(または容量)です。
スライスの容量は、基底配列内で割り当てられているスペースがある要素の数です。常に次の関係が成り立ちます:
0 <= len(s) <= cap(s)
nil
スライス、マップ、またはチャネルの長さは0です。nil
スライスまたはチャネルの容量は0です。
式 len(s)
は、s
が文字列定数の場合、定数となります。式 len(s)
と cap(s)
は、s
の型が配列または配列へのポインタで、式 s
にチャネル受信や(非定数の)関数呼び出しが含まれていない場合、定数となります。この場合、s
は評価されません。それ以外の場合、len
と cap
の呼び出しは定数ではなく、s
は評価されます。
const (
c1 = imag(2i) // imag(2i) = 2.0 は定数
c2 = len([10]float64{2}) // [10]float64{2} には関数呼び出しが含まれていない
c3 = len([10]float64{c1}) // [10]float64{c1} には関数呼び出しが含まれていない
c4 = len([10]float64{imag(2i)}) // imag(2i) は定数で、関数呼び出しは発行されない
c5 = len([10]float64{imag(z)}) // 無効: imag(z) は(非定数の)関数呼び出し
)
var z complex128
スライス、マップ、チャネルの作成 #
組み込み関数 make
は型 T
(スライス、マップ、チャネル型、または型パラメータでなければならない)を取り、オプションで型固有の式リストが続きます。型 T
の値を返します(*T
ではありません)。メモリは初期値のセクションで説明されているように初期化されます。
呼び出し 型 T 結果
make(T, n) スライス 長さ n、容量 n の型 T のスライス
make(T, n, m) スライス 長さ n、容量 m の型 T のスライス
make(T) マップ 型 T のマップ
make(T, n) マップ 約 n 個の要素の初期スペースを持つ型 T のマップ
make(T) チャネル 型 T の非バッファ付きチャネル
make(T, n) チャネル 型 T のバッファサイズ n のバッファ付きチャネル
make(T, n) 型パラメータ 下記参照
make(T, n, m) 型パラメータ 下記参照
最初の引数が型パラメータである場合、そのタイプセット内のすべての型は同じ基本型を持たなければならず、それはスライスまたはマップ型でなければなりません。または、チャネル型がある場合は、チャネル型のみでなければならず、それらはすべて同じ要素型を持ち、チャネルの方向が競合してはいけません。
サイズ引数 n
と m
はそれぞれ整数型、整数型のみを含むタイプセットを持つ、または型なし定数でなければなりません。定数サイズ引数は非負で、int
型の値で表現可能でなければなりません。型なし定数の場合、それには int
型が与えられます。n
と m
の両方が提供され、定数である場合、n
は m
より大きくてはいけません。スライスとチャネルの場合、実行時に n
が負または m
より大きい場合、実行時パニックが発生します。
s := make([]int, 10, 100) // len(s) == 10, cap(s) == 100 のスライス
s := make([]int, 1e3) // len(s) == cap(s) == 1000 のスライス
s := make([]int, 1<<63) // 不正: len(s) は int 型の値で表現できない
s := make([]int, 10, 0) // 不正: len(s) > cap(s)
c := make(chan int, 10) // バッファサイズ 10 のチャネル
m := make(map[string]int, 100) // 約 100 要素の初期スペースを持つマップ
サイズヒント n
を持つマップ型で make
を呼び出すと、n
マップ要素を保持するための初期スペースを持つマップが作成されます。正確な動作は実装依存です。
最小値と最大値 #
組み込み関数 min
と max
は、順序付けられた型の固定数の引数の最小値または最大値をそれぞれ計算します。少なくとも1つの引数が必要です [Go 1.21]。
演算子と同じ型ルールが適用されます:順序付けられた引数 x
と y
に対して、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 は x と y の小さい方
m := max(x, y, 10) // m は x と y の大きい方だが、少なくとも 10
c := max(1, 2.0, 10) // c == 10.0 (浮動小数点型)
f := max(0, float32(x)) // f の型は float32
var s []string
_ = min(s...) // 無効: スライス引数は許可されていない
t := max("", "foo", "bar") // t == "foo" (文字列型)
数値引数の場合、すべての NaN が等しいと仮定すると、min
と max
は可換性と結合性があります:
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 // 負のゼロは(非負の)ゼロより小さい
-Inf y -Inf y // 負の無限大は他のどの数よりも小さい
+Inf y y +Inf // 正の無限大は他のどの数よりも大きい
NaN y NaN NaN // 引数のいずれかが NaN の場合、結果は NaN
文字列引数の場合、min
の結果は(または max
の場合は最大値)値が最小の最初の引数で、バイト単位でレキシカルに比較されます:
min(x, y) == x <= y の場合 x、そうでなければ 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=0
、b=0.0
)、場所のアドレスを含む型 *S
の値を返します。
パニックの処理 #
2つの組み込み関数 panic
と recover
は、実行時パニックとプログラム定義のエラー条件の報告と処理を支援します。
func panic(interface{})
func recover() interface{}
関数 F
を実行中に、panic
の明示的な呼び出しまたは実行時パニックが発生すると、F
の実行は終了します。F
により遅延された関数は通常通り実行されます。次に、F
の呼び出し元により遅延された関数が実行され、実行中のゴルーチンのトップレベル関数まで遡ります。その時点でプログラムは終了し、panic
への引数の値を含むエラー条件が報告されます。この終了シーケンスはパニックと呼ばれます。
panic(42)
panic("unreachable")
panic(Error("cannot parse"))
recover
関数を使用すると、プログラムはパニック状態のゴルーチンの動作を管理できます。関数 G
が recover
を呼び出す関数 D
を遅延させ、G
が実行されているのと同じゴルーチン上の関数でパニックが発生したとします。遅延関数の実行が D
に達すると、D
の recover
呼び出しの戻り値は panic
の呼び出しに渡された値になります。D
が新しい panic
を開始せずに正常に戻ると、パニックシーケンスは停止します。その場合、G
と panic
の呼び出しの間に呼び出された関数の状態は破棄され、通常の実行が再開されます。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 は正常に実行される
if x := recover(); x != nil {
log.Printf("run time panic: %v", x)
}
}()
log.Println("start")
g()
}
ブートストラップ #
現在の実装では、ブートストラップ中に役立ついくつかの組み込み関数が提供されています。これらの関数は完全性のために文書化されていますが、言語に残ることは保証されていません。これらは結果を返しません。
関数 動作
print すべての引数を出力する。引数のフォーマットは実装固有
println print と同様だが、引数間にスペースを入れ、最後に改行を追加
実装制限:print
と println
は任意の引数型を受け入れる必要はありませんが、ブール値、数値、および文字列型の出力はサポートされていなければなりません。
パッケージ #
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の解釈は実装依存ですが、 通常はコンパイルされたパッケージの完全なファイル名のサブストリングであり、 インストールされたパッケージのリポジトリに対する相対パスである場合があります。
実装の制限: コンパイラはImportPathを、
UnicodeのL、M、N、P、S一般カテゴリ(スペースなしのグラフィック文字)に
属する文字のみを使用した空でない文字列に制限し、
!"#$%&'()*,:;<=>?[\]^
{|}`の文字およびUnicodeの置換文字U+FFFDを除外することができます。
パッケージ句package math
を含む、関数Sin
をエクスポートするコンパイル済みパッケージが、
"lib/math"
というファイルにインストールされているとします。
この表は、様々な種類のインポート宣言後にそのパッケージをインポートするファイルで、
Sin
にどのようにアクセスするかを示しています。
インポート宣言 Sinのローカル名
import "lib/math" math.Sin
import m "lib/math" m.Sin
import . "lib/math" Sin
インポート宣言は、インポートする側とされる側のパッケージ間の依存関係を宣言します。 パッケージが自分自身を直接的または間接的にインポートすることは違法であり、 エクスポートされた識別子を参照せずにパッケージを直接インポートすることも違法です。 パッケージをその副作用(初期化)のためだけにインポートするには、 明示的なパッケージ名としてブランク識別子を使用します:
import _ "lib/math"
パッケージの例 #
以下は、並行素数ふるいを実装する完全なGoパッケージです。
package main
import "fmt"
// 'ch'チャネルに2, 3, 4, ...の数列を送信
func generate(ch chan<- int) {
for i := 2; ; i++ {
ch <- i // 'i'を'ch'チャネルに送信
}
}
// 'src'チャネルから'dst'チャネルに値をコピーし、
// 'prime'で割り切れるものを除去
func filter(src <-chan int, dst chan<- int, prime int) {
for i := range src { // 'src'から受信した値でループ
if i%prime != 0 {
dst <- i // 'i'を'dst'チャネルに送信
}
}
}
// 素数ふるい: フィルタープロセスをデイジーチェーンで連結
func sieve() {
ch := make(chan int) // 新しいチャネルを作成
go generate(ch) // generate()をサブプロセスとして開始
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とbは一緒に初期化され、xが初期化される前に初期化される
パッケージの初期化の目的では、ブランク識別子は宣言内の他の変数と同様に扱われます。
複数のファイルで宣言された変数の宣言順序は、ファイルがコンパイラに提示される順序によって決まります:最初のファイルで宣言された変数は、2番目のファイルで宣言された変数の前に宣言され、以下同様です。再現可能な初期化動作を確保するために、ビルドシステムは、同じパッケージに属する複数のファイルを辞書順のファイル名順でコンパイラに提示することが推奨されます。
依存関係分析は、変数の実際の値に依存せず、ソース内のそれらへの語彙的な参照のみに依存し、推移的に分析されます。例えば、変数x
の初期化式が変数y
を参照する関数の本体を参照している場合、x
はy
に依存します。具体的には:
- 変数または関数への参照は、その変数または関数を示す識別子です。
- メソッド
m
への参照は、t.m
形式のメソッド値またはメソッド式です。ここでt
の(静的)型はインターフェース型ではなく、メソッドm
はt
のメソッドセット内にあります。結果の関数値t.m
が呼び出されるかどうかは重要ではありません。 - 変数、関数、またはメソッド
x
は、x
の初期化式または本体(関数とメソッドの場合)にy
への参照、またはy
に依存する関数やメソッドへの参照が含まれている場合、変数y
に依存します。
例えば、次の宣言が与えられた場合:
var (
a = c + b // == 9
b = f() // == 4
c = f() // == 5
d = 3 // == 初期化が完了した後は5
)
func f() int {
d++
return d
}
初期化順序はd
、b
、c
、a
です。初期化式の部分式の順序は関係ないことに注意してください:この例ではa = c + b
とa = b + c
は同じ初期化順序になります。
依存関係分析はパッケージごとに実行されます。現在のパッケージで宣言された変数、関数、および(非インターフェース)メソッドを参照する参照のみが考慮されます。他の隠れたデータ依存関係が変数間に存在する場合、それらの変数間の初期化順序は指定されていません。
例えば、次の宣言が与えられた場合:
var x = I(T{}).ab() // xはaとbに未検出の隠れた依存関係がある
var _ = sideEffect() // x、a、またはbとは無関係
var a = b
var b = 42
type I interface { ab() []int }
type T struct{}
func (T) ab() []int { return []int{a, b} }
変数a
はb
の後に初期化されますが、x
がb
の前に初期化されるか、b
とa
の間に初期化されるか、またはa
の後に初期化されるか、そして同様にsideEffect()
が呼び出される瞬間(x
が初期化される前か後か)は指定されていません。
変数は、パッケージブロックで宣言された引数と結果パラメータを持たないinit
という名前の関数を使用して初期化することもできます。
func init() { … }
そのような関数は、単一のソースファイル内でも、パッケージごとに複数定義できます。パッケージブロックでは、init
識別子はinit
関数を宣言するためにのみ使用できますが、識別子自体は宣言されていません。したがって、init
関数はプログラムのどこからも参照できません。
パッケージ全体は、すべてのパッケージレベル変数に初期値を割り当て、その後、ソース内に現れる順序ですべてのinit
関数を呼び出すことによって初期化されます。これらの関数は、コンパイラに提示されるように、おそらく複数のファイルに存在します。
プログラムの初期化 #
完全なプログラムのパッケージは、一度に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
// およびおそらく他のメソッド
}
システムに関する考慮事項 #
unsafe
パッケージ #
組み込みパッケージ unsafe
はコンパイラに認識され、インポートパス "unsafe"
を通じてアクセス可能です。このパッケージは型システムに違反する操作を含む低レベルプログラミング機能を提供します。unsafe
パッケージを使用するパッケージは、型安全性を手動で検証する必要があり、移植性が保証されない場合があります。パッケージは以下のインターフェイスを提供します:
package unsafe
type ArbitraryType int // 任意のGo型を表す省略形;実際の型ではない
type Pointer *ArbitraryType
func Alignof(variable ArbitraryType) uintptr
func Offsetof(selector ArbitraryType) uintptr
func Sizeof(variable ArbitraryType) uintptr
type IntegerType int // 整数型を表す省略形;実際の型ではない
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
である型に変換でき、その逆も可能です。それぞれの型が型パラメータである場合、それぞれの型セット内のすべての型は同じ基底型を持つ必要があり、それぞれ 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
を取り、あたかも var v = x
を通じて変数 v
が宣言されたかのように、仮想的な変数 v
のアラインメントまたはサイズをそれぞれ返します。
関数 Offsetof
は(場合によっては括弧で囲まれた)セレクタ s.f
を取ります。これは s
または *s
で示される構造体のフィールド f
を示し、構造体のアドレスを基準としたフィールドのオフセットをバイト単位で返します。f
が埋め込まれたフィールドの場合、構造体のフィールドを通じてポインタによる間接参照なしで到達可能でなければなりません。フィールド f
を持つ構造体 s
の場合:
uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f) == uintptr(unsafe.Pointer(&s.f))
コンピュータアーキテクチャでは、メモリアドレスがアラインメントされていることが要求される場合があります。つまり、変数のアドレスが変数の型のアラインメントという要素の倍数であることが求められます。関数 Alignof
は任意の型の変数を示す式を取り、バイト単位で変数(の型)のアラインメントを返します。変数 x
の場合:
uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0
型 T
の(変数)は、T
が型パラメータである場合、または可変サイズの要素やフィールドを含む配列または構造体型である場合に可変サイズを持ちます。それ以外の場合、サイズは定数です。Alignof
、Offsetof
、Sizeof
の呼び出しは、引数(または Offsetof
のセレクタ式 s.f
における構造体 s
)が定数サイズの型である場合、型 uintptr
の定数式です。
関数 Add
は ptr
に len
を加算し、更新されたポインタ 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。
サイズとアラインメントの保証 #
数値型については、以下のサイズが保証されています:
型 サイズ(バイト)
byte, uint8, int8 1
uint16, int16 2
uint32, int32, float32 4
uint64, int64, float64, complex64 8
complex128 16
以下の最小アラインメント特性が保証されています:
任意の型の変数
x
について:unsafe.Alignof(x)
は少なくとも 1 です。構造体型の変数
x
について:unsafe.Alignof(x)
は、x
の各フィールドf
に対するunsafe.Alignof(x.f)
の値のうち最大のものですが、少なくとも 1 です。配列型の変数
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 #
- 整数リテラルは、2進数と8進数リテラルにそれぞれ接頭辞
0b
、0B
、0o
、および0O
を使用できるようになりました。 - 16進数の浮動小数点リテラルは、接頭辞
0x
と0X
を使用して記述できるようになりました。 - 虚数接尾辞
i
は、10進数リテラルだけでなく、任意の(2進数、10進数、16進数)整数または浮動小数点リテラルと共に使用できるようになりました。 - 数値リテラルの桁はアンダースコア
_
を使用して区切る(グループ化する)ことができるようになりました。 - シフト操作におけるシフトカウントは、符号付き整数型でもよくなりました。
Go 1.14 #
- 異なる埋め込みインターフェースを通じてメソッドを複数回埋め込むことはエラーではなくなりました。
Go 1.17 #
Go 1.18 #
Go 1.18リリースでは、多相関数と型(「ジェネリクス」)が言語に追加されました。具体的には:
- 演算子と句読点のセットに新しいトークン
~
が追加されました。 - 関数と型の宣言で型パラメータを宣言できるようになりました。
- インターフェース型は、(インターフェースの型名だけでなく)任意の型や、和集合、
~T
型要素を埋め込むことができるようになりました。 - 事前宣言された型のセットに、新しい型
any
とcomparable
が追加されました。
Go 1.20 #
- スライスと配列の要素型が一致し、配列がスライスより長くない場合、スライスを配列に変換できるようになりました。
- ビルトインパッケージ
unsafe
に新しい関数SliceData
、String
、およびStringData
が追加されました。 - 型引数が厳密に比較可能でない場合でも、比較可能な型(通常のインターフェースなど)は
comparable
制約を満たすことができるようになりました。
Go 1.21 #
- 事前宣言された関数のセットに、新しい関数
min
、max
、およびclear
が追加されました。 - 型推論はインターフェースメソッドの型を推論に使用するようになりました。また、変数に割り当てられたジェネリック関数や、他の(場合によってはジェネリックな)関数に引数として渡されたジェネリック関数の型引数も推論するようになりました。
Go 1.22 #
- “for"文では、各イテレーションが同じ変数を共有するのではなく、独自のイテレーション変数セットを持つようになりました。
- “range"節を持つ"for"文は、ゼロから上限までの整数値を反復処理できるようになりました。
Go 1.23 #
- “range"節を持つ"for"文は、range式としてイテレータ関数を受け入れるようになりました。
Go 1.24 #
型の統一規則 #
型の統一規則は、2つの型が統一されるかどうか、およびその方法を記述します。正確な詳細はGo実装に関連し、エラーメッセージの詳細(コンパイラが型推論エラーまたは他のエラーを報告するかどうかなど)に影響し、通常とは異なるコード状況で型推論が失敗する理由を説明するかもしれません。しかし、概してこれらの規則はGoコードを書く際に無視できます:型推論は主に「期待通りに動作する」ように設計されており、統一規則はそれに応じて微調整されています。
型の統一はマッチングモードによって制御され、これは厳密または緩やかのいずれかです。統一が複合型構造を再帰的に降下する際、型の要素に使用されるマッチングモード(要素マッチングモード)は、2つの型が代入可能性(≡ₐ
)のために統一される場合を除いて、マッチングモードと同じままです:この場合、マッチングモードは最上位では緩やかですが、要素型では厳密に変わります。これは、型が代入可能であるために同一である必要がないという事実を反映しています。
バインドされていない型パラメータではない2つの型は、次の条件のいずれかが真である場合に厳密に統一されます:
- 両方の型が同一である。
- 両方の型が同一の構造を持ち、その要素型が厳密に統一される。
- 正確に1つの型がバインドされていない型パラメータであり、その型セット内のすべての型が
≡ₐ
の統一規則(最上位では緩やかな統一、要素型では厳密な統一)に従って他の型と統一される。
両方の型がバインドされた型パラメータである場合、それらは与えられたマッチングモードに従って統一されます。条件は:
- 両方の型パラメータが同一である。
- 型パラメータの最大1つが既知の型引数を持つ。この場合、型パラメータは結合されます:両方が同じ型引数を表します。どちらの型パラメータもまだ既知の型引数を持たない場合、一方の型パラメータに対して推論される将来の型引数は、両方に対して同時に推論されます。
- 両方の型パラメータが既知の型引数を持ち、型引数が与えられたマッチングモードに従って統一される。
単一のバインドされた型パラメータP
と別の型T
は、与えられたマッチングモードに従って統一されます。条件は:
P
が既知の型引数を持たない。この場合、T
はP
の型引数として推論されます。P
が既知の型引数A
を持ち、A
とT
が与えられたマッチングモードに従って統一され、次の条件のいずれかが真である:
最後に、バインドされた型パラメータではない2つの型は、次の条件のいずれかが真である場合、緩やかに(そして要素マッチングモードに従って)統一されます:
- 両方の型が厳密に統一される。
- 一方の型が定義された型であり、もう一方の型が型リテラルであるが、インターフェースではなく、それらの基底型が要素マッチングモードに従って統一される。
- 両方の型がインターフェース(ただし型パラメータではない)であり、同一の型項を持ち、両方とも事前宣言された型
comparable
を埋め込むか、両方とも埋め込まず、対応するメソッド型が厳密に統一され、一方のインターフェースのメソッドセットがもう一方のインターフェースのメソッドセットの部分集合である。 - 一方の型のみがインターフェース(ただし型パラメータではない)であり、2つの型の対応するメソッドが要素マッチングモードに従って統一され、インターフェースのメソッドセットがもう一方の型のメソッドセットの部分集合である。
- 両方の型が同じ構造を持ち、それらの要素型が要素マッチングモードに従って統一される。