このドキュメントは
The Go Programming Language Specification
(http://golang.org/doc/go_spec.html)の翻訳です。


はじめに
表記方法
ソースの文字コード
文字
字と数字
字句要素
コメント
トークン
識別子
キーワード
演算子と区切り文字
整数リテラル
浮動小数点リテラル
文字リテラル
文字列リテラル
定数
論理値型
数値型
文字列型
配列型
スライス型
構造体型
ポインタ型
関数型
インタフェース型
マップ型
チャネル型
型と値の特性
型の同一性と互換性
代入の適合性
比較の適合性
ブロック
宣言とスコープ
ラベルのスコープ
事前宣言済み識別子
エクスポートされた識別子
ブランク識別子
定数の宣言
Iota
型の宣言
変数の宣言
省略形式による変数の宣言
関数の宣言
メソッドの宣言
オペランド
限定付き識別子
複合リテラル
関数リテラル
基本式
セレクタ
インデックス
スライス
型アサーション
呼び出し
…パラメータの解析
演算子
演算子の優先順位
算術演算子
整数のオーバフロー
比較演算子
論理演算子
アドレス演算子
通信演算子
メソッド式
変換
定数式
評価の順番
ステートメント
空ステートメント
ラベル付きステートメント
式ステートメント
インクリメント/デクリメントステートメント
代入
ifステートメント
switchステートメント
forステートメント
goステートメント
selectステートメント
returnステートメント
breakステートメント
continueステートメント
gotoステートメント
fallthroughステートメント
deferステートメント
組み込み関数
close、closed
長さ、キャパシティ
メモリの割当
スライス、マップ、チャネルの作成
ブートストラッピング
パッケージ
ソースファイルの構成
パッケージ文
インポート宣言
パッケージサンプル
プログラムの初期化と実行
ゼロ値
プログラムの実行
システム考察
unsafeパッケージ
サイズとアライメントの保証
実装との差異 – TODO

はじめに

この文書はプログラミング言語Goのリファレンスマニュアルです。その他の情報、文書はhttp://golang.org を参照ください。

Go言語はシステムプログラミングを念頭に置いた多目的言語です。
この言語には、強い型付け、ガーベージコレクション、並列処理のサポート機能があります。
プログラムはパッケージという単位から成り、パッケージはそれを利用するプログラムに管理されたアクセスを提供します。
今現在は、従来からあるコンパイル/リンクモデルによる実行バイナリ生成方式を採っています。

Go言語の文法はコンパクトかつ規則的で、IDE(統合開発環境)のような自動ツールにとって解析しやすい言語となっています。

表記方法

この文書はEBNF(Extended Backus-Naur Form)の記法に準拠します。
下はEBNFをEBNF自身で表したものです。

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

EBNFでは語句および、下で優先度順に示された演算子による式を組み合わせて定義を行います。

|   いずれか
()  グルーピング
[]  オプション(0 または 1回)
{}  繰り返し (0 ~ n回)

イコールの左辺の名称が小文字場合は、その名称はトークンを識別するために使われます。
名称を他のEBNF文の右辺で使用する場合は、大文字と小文字を混在させて記述します。
シンボルはダブルクォート""、またはバッククォート``でくくられます。

「a ... b」形式はaからbまでのうちの、いずれかの文字のセットを表します。

ソースの文字コード

ソースの文字コードは、UTF-8でエンコードされたユニコード文字です。テキストは正規化されないため、あるアクセント付きコードポイントは、それと同 じ文字ではあるがアクセント記号とアルファベットを結合して造られた文字とは別のものとして扱われます。これらは2つのコードポイントとみなされます。
このドキュメントでは「ユニコードのコードポイント」と記述する代わりに、文字(キャラクタ)という言葉を使用します。

各コードポイントはそれぞれ異なる文字です。たとえば同じアルファベットの文字でも大文字・小文字が違えば別の文字として扱われます。

文字

次の書き方は、特定のユニコード文字クラスを示すときに用いられます。

unicode_char   = /* an arbitrary Unicode code point */ .
unicode_letter = /* a Unicode code point classified as "Letter" */ .
unicode_digit  = /* a Unicode code point classified as "Digit" */ .

The Unicode Standard 5.1のセクション4.5 「General Category—Normative」において、文字カテゴリセットが定義されています。
Go言語ではこれらカテゴリのうちLu, Ll, Lt, Lm, Loをユニコード字(レター)、Ndをユニコード数字として扱います。

字と数字

アンダースコア_ (U+005F)は、字(レター)とみなされます。

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

字句要素

コメント

コメントには2種類の書式があります。
ひとつ目の書式は文字シーケンス//で始まり、行の終わりまでがコメントとなります。ふたつ目は文字シーケンス/*で始まり、文字シーケンス*/までがコメントとなります。コメントを入れ子にすることはできません。

トークン

トークンには識別子、キーワード、演算子と区切り文字、リテラルの4つのクラスがあります。
ホワイトスペースはスペース(U+0020)、水平タブ (U+0009)、キャリッジリターン (U+000D)、改行 (U+000A)です。ホワイトスペースは、それがなければ一つのトークンとして結合されてしまうトークンを分割するほかは無視されます。コメントはホワ イトスペースと同等に扱われます。入力をトークンに分割する際、次のトークンは長い文字列シーケンスから成る有効なトークンです。

識別子

識別子は変数や型といったプログラムの実体に対し名前をつけます。
識別子は一文字以上の字(レター)と数字から構成されます。先頭一文字は字でなくてはなりません。

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

演算子と区切り文字

下に示す文字シーケンスは、演算子、区切り文字および、その他の特別なトークンです。

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

整数リテラル

整数リテラルは整数定数を表す数字の並びです。
10進数以外の値を表すにはプレフィックスをつけます。0は8進数で、0xまたは0Xを付けると16進数になります。16進数の場合、文字a~fA~Fを10~15の値を表すために使います。

int_lit     = decimal_lit | octal_lit | hex_lit .
decimal_lit = ( "1" ... "9" ) { decimal_digit } .
octal_lit   = "0" { octal_digit } .
hex_lit     = "0" ( "x" | "X" ) hex_digit { hex_digit } .
42
0600
0xBadFace
170141183460469231731687303715884105727

浮動小数点リテラル

浮動小数点リテラルは浮動小数点定数を表す小数で、整数部・小数点・小数部・指数部を持ちます。
整数部と小数部は10進数から成ります。指数部はeまたはEと、それに続く符号(オプション)と10進数の指数です。
整数部と分数部のどちらかは省略でき、また小数点か指数部のどちらかも省略できます。

float_lit = decimals "." [ decimals ] [ exponent ] |
            decimals exponent |
            "." decimals [ exponent ] .
decimals  = decimal_digit { decimal_digit } .
exponent  = ( "e" | "E" ) [ "+" | "-" ] decimals .
0.
2.71828
1.e+0
6.67428e-11
1E6
.25
.12345E+5

文字リテラル

文字リテラルは整数定数、概して一文字以上の文字をシングルクォートでくくりユニコードのコードポイントを表現します。

クォート内にはシングルクォートと改行を除くどんな文字も記述できます。バックスラッシュを使用したマルチ文字シーケンスの各書式を使ってエンコードすることで、シングルクォート文字自体も表せます。

クォートを使うと簡単に一文字を表すことができます。これはGo言語のソースがUTF-8エンコードされたユニコード文字であり、UTF-8エンコードされている複数のバイト列であってもひとつの整数値を表せるからです。
たとえば、リテラル'a'はリテラルa、ユニコード U+0061、値0x61を表す1バイトの値です。一方'ä' はリテラルa-dieresis、 U+00E4、値0xe4を表しますが、UTF-8エンコードでは2バイト (0xc3 0xa4)です。

バックスラッシュによるエスケープを使って任意の値をアスキー文字として表すことができます。定数として整数値を表すには次の4通りの方法があります。\xに続いた2つの16進数文字、\uに続いた4つの16進数文字、\Uに続いた8つの16進数文字、\に続いた3つの8進数文字です。それぞれのリテラルの値は、基数に対応した数字で表される値です。

これらが表す値はすべて整数ですが、値の範囲がそれぞれ異なります。8進数のエスケープで表す値は0-255の範囲内でなければなりません。16真数のエスケープはその書式を満たす0-255が範囲です。\u\Uはユニコードのコードポイントを表すため、いくつかの値は不正な値となります。特に0x10FFFFより大きい数と上位サロゲートは不正な値です。

バックスラッシュで特定の一文字をエスケープすることで特別な値を表します。

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

これ以外のバックスラッシュで始まるシーケンスは文字リテラル内では不正となります。

char_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'

文字列リテラル

文字列リテラルは結合された文字シーケンスから成る文字列定数を表します。
文字列リテラルには未加工(raw)文字列リテラルと、解釈有(interpreted)文字列リテラルの2通りの記述方法があります。

未加工文字列リテラルは、バッククォート``で囲まれた文字シーケンスです。クォート内にはバッククォート以外の文字すべてを記述できます。この未加工文字列リテラルの示す値は、クォート内の解釈されない文字から成る文字列で、バックスラッシュも特別な意味を持たず、また文字列は複数行にまたがることも可能です。

解釈有文字列リテラルはダブルクォート""で囲まれた文字シーケンスです。クォートで囲まれたテキストは複数行にまたがることはできず、文字リテラルとおなじくバックスラッシュによるエスケープを解釈した結果がリテラルの値となります。(\'は使えませんが、\"は使用できます。)3つの数字から成る8進数(\000) 、2つの数字から成る16進数 (\x00)のエスケープは個々のバイトを表します。他のすべてのエスケープは(マルチバイトも含め) UTF-8エンコードされた個別の文字を表します。したがって文字列リテラル \377\xFFは、1バイトの値0xFF=255を表します。一方 ÿ\u00FF\U000000FF、\xc3\xbfは、文字U+00FFをUTF8エンコーディングした値である2バイト0xc3 0xbfを表します。

文字列シーケンスを並べたものは結合されて、ひとつの文字列となります。

StringLit              = string_lit { string_lit } .
string_lit             = raw_string_lit | interpreted_string_lit .
raw_string_lit         = "`" { unicode_char } "`" .
interpreted_string_lit = `"` { unicode_value | byte_value } `"` .
`abc`  // same as "abc"
`\n
\n`    // same as "\\n\n\\n"
"\n"
""
"Hello, world!\n"
"日本語"
"\u65e5本\U00008a9e"
"\xff\u00FF"
"Alea iacta est."
"Alea " /* The die */ `iacta est` /* is cast */ "."  // same as "Alea iacta est."

次の例は、すべて同じ文字列を表しています。

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

ソースコード内に、アクセントとアルファベットを結合した場合のように2つのコードポイントから成る「文字リテラル」があるとエラーとなります。こ れは文字リテラルがひとつのコードポイントを表すためです。ただし「文字列リテラル」には2つのコードポイントから成る文字を記述できます。

定数

定数にはブーリアン定数、整数定数、浮動小数点定数、文字列定数があります。整数定数と浮動小数点定数はまとめて数値定数とも呼ばれます。

定数の値は整数リテラル浮動小数点リテラル文字リテラル文字列リテラル、定数を示す識別子、定数式unsafe.Sizeofのような組み込み関数からの戻り値、配列のcapまたはlen、文字列定数のlenによって表されます。ブーリアンの真理値は事前宣言済み定数trueおよびfalseによって表されます。事前宣言済み識別子iotaは整数リテラルを表します。

数値定数はサイズを持たず、またオーバーフローすることがない任意精度の値を表します。

定数にはを持つもの、持たないものがあります。リテラル定数、truefalseiota、および”定数式内の全オペランドが型を持たない定数”のときは、定数は型を持ちません。

定数は定数の宣言または変換によって明示的に、または変数の宣言代入のオペランドで使われるときに暗黙的に、型が付与されます。ただし定数の値が、各型の値を正確に表現することができなければエラーとなります。例えば、3.0はすべての整数型もしくは浮動小数点型にすることができます。しかし、2147483648.0(1<<31と同値)はfloat32型、float64型、uint32型にはできますが、int32型やstring型にはできません。

実装の制約:コンパイラは数値定数の内部表現に最小でもマシンの倍のビット数を使います。浮動小数点の値については、仮数部と指数部それぞれが倍になります。

型は、その型を持つ値に対し具体的な値と操作の組み合わせを規定します。型を表すには型の(パッケージ名を伴なう場合もある)名称(§限定付き識別子、 §型の宣言) または型リテラルで記述します。事前に定義した型から別の新しい型を作成することもできます。

Type      = TypeName | TypeLit | "(" Type ")" .
TypeName  = QualifiedIdent.
TypeLit   = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
	    SliceType | MapType | ChannelType .

論理値型、数値型、文字列型として事前宣言済みの型が用意されています。 コンポジット型(配列、構造体、ポインタ、関数、インタフェース、スライス、マップ、チャネル型)は型リテラルを使って作られます。

型は、その型と関連付けられたメソッド群を持ちます (§インタフェース型、§メソッドの宣言) 。インタフェース型のメソッド群はインタフェース自身です。インタフェース以外の型を仮にTとすると、そのT型のメソッド群はT型のレシーバを持ったすべてのメソッドです。 ポインタ型*Tと対応するメソッド群は、*TまたはT型のレシーバを持ったすべてのメソッドです。(すなわち、T型が持つメソッド群が含まれます。)また各メソッドは、メソッド群内におけるユニークな名前を持っています。

変数の静的な型(または適正な型)は、変数の宣言時に指定された型です。インタフェース型の変数は、他の型とは違って動的な型を持っており、実行時にその変数に格納された値の型が実際の型となります。この動的な型はプログラム実行中に値が入れ替わったとしても、常にインタフェース変数の静的な型と互換のある値が代入されます。非インタフェース型において、動的な型と静的な型は常に一致します。

論理値型

論理値型は、事前宣言済み定数trueまたはfalseによる論理値を表現します。論理値型として事前宣言済みの型はboolです。

数値型

数値型は、整数または浮動小数点の値を表現します。アーキテクチャに依存しない数値型として事前宣言済みの型は、

uint8    符号なし  8-ビット 整数 (0 to 255)
uint16   符号なし 16-ビット 整数 (0 to 65535)
uint32   符号なし 32-ビット 整数 (0 to 4294967295)
uint64   符号なし 64-ビット 整数 (0 to 18446744073709551615)

int8     符号あり  8-ビット 整数 (-128 to 127)
int16    符号あり 16-ビット 整数 (-32768 to 32767)
int32    符号あり 32-ビット 整数 (-2147483648 to 2147483647)
int64    符号あり 64-ビット 整数 (-9223372036854775808 to 9223372036854775807)

float32  IEEE-754 32-ビット 浮動小数値
float64  IEEE-754 64-ビット 浮動小数値

byte     uint8の別名

整数型は一般的なバイナリ形式であり、「n-ビット 整数」型の値はnビットのサイズを持ち、負の値は絶対値を2の補数で表現します。

実装に依存したサイズを持つ数値型も用意されています。

uint     32 または 64 ビット
int      32 または 64 ビット
float    32 または 64 ビット
uintptr  ポインタの値をそのまま格納するのに充分な大きさの符号なし整数

uint8の別名であるbyte型を除いて、数値型はサイズが同じであってもそれぞれが別の型です。これは移植時に問題が起きないようにするためです。互換性のない数値型どうしを式や代入に使用するときは変換が必要となります。たとえばint32intが、あるアーキテクチャ上で同一のサイズであったとしても、これらは同じ型ではありません。

文字列型

文字列型は文字列の値を表現します。文字列はbyteの配列のように振舞いますが、値は不変です。つまり一度作成された以降は、文字列の内容を変更できません。文字列型として事前宣言済みの型はstringです。

文字列型の要素はbyte型データを持っているため、一般的なインデックスを使ったアクセスが可能です。ただし要素のアドレスを取得することはできません。すなわちs[i]が、ある文字列のi番目のバイトであるとして、&s[i]とすることはできません。文字列の長さは、組み込み関数lenを使用して調べることができます。文字列sがリテラルであれば、文字列長はコンパイル時に定数となります。

配列型

配列は同一の型(要素型)を持つ要素を並べたものです。要素の数は長さ(length)と呼ばれます。この値はマイナス値には成り得ません。

ArrayType   = "[" ArrayLength "]" ElementType .
ArrayLength = Expression .
ElementType = Type .

配列の長さ情報は、その配列型の一部であり定数式で使うことができます。配列aの長さは、組み込み関数len(a)を使用して調べることができ、コンパイル時に定数となります。配列の要素は、0からlen(a)-1までの整数によるインデックスで指し示すことができます(§インデックス)。

[32]byte
[2*N] struct { x, y int32 }
[1000]*float64

スライス型

スライスはある配列内の連続した領域への参照であり、スライスの内容はその配列の要素の並びです。スライス型は、その要素型を持つ配列すべてのスライスの集合を表します。スライス型の値はnilをとることがあります。

SliceType = "[" "]" ElementType .

配列のように、スライスはインデックスによる指定が可能で長さを持ちます。スライスsの長さは、組み込み関数len(s)を使用して調べることができますが、配列の場合とは異なり長さは実行中に変わることがあります。要素は0からlen(s)-1までの整数によるインデックスで指し示すことができます(§インデックス)。

スライスは一度初期化されると、その要素を所有する配列との関連を常に保ちます。そのため、スライスは元となった配列および、同一の配列から作られた別のスライスとメモリを共有します。これとは対照的に、異なる配列は常に異なるメモリ領域を有します。

スライスの元になった配列は、スライスの最後の要素以降にも要素を持つことがあります。キャパシティとは範囲の大きさであり、スライスの長さと、元の配列のスライス以降の長さとの合計です。スライスから新しく『スライスする』ことによって、最大でキャパシティの値までの長さのスライスを作ることができます(§スライス)。スライスaのキャパシティは組み込み関数cap(a)で調べることができます。len()cap()の関係は次のとおりです。

0 <= len(a) <= cap(a)

初期化されていないスライスの値はnilです。nilスライスの長さとキャパシティはともに0です。初期化済みの新しいスライスを作るには組み込み関数makeを使用します。make関数はパラメータにスライスの型と長さ、オプションでキャパシティをとります。

make([]T, length)
make([]T, length, capacity)

make()は隠された配列を新たに割り当て、それを参照するスライスを返します。次のようにします。

make([]T, length, capacity)

これは配列を割り当て、そこからスライスを作成するのと同じことなので、ゆえに次の2つの例は結果として同じスライスになります。

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

構造体型

構造体は、フィールドと呼ばれる要素の集まりで、それぞれが名前と型を持っています。フィールド名は明示的(IdentifierList)、または暗黙的(AnonymousField)に指定されます。ブランクフィールドを除いて、フィールド名は構造体内でユニークである必要があります。

StructType     = "struct" "{" [ FieldDeclList ] "}" .
FieldDeclList  = FieldDecl { ";" FieldDecl } [ ";" ] .
FieldDecl      = (IdentifierList Type | AnonymousField) [ Tag ] .
AnonymousField = [ "*" ] TypeName .
Tag            = StringLit .
// 空の構造体
struct {}

// 6フィールド持つ構造体
struct {
	x, y int;
	u float;
	_ float;  // パディング
	A *[]int;
	F func();
}

フィールドが型だけでフィールド名を指定せずに宣言されたときは匿名フィールドとなります。このような匿名フィールドの型は、型名Tまたは型へのポインタ*Tのように記述しなければなりません。T自体はポインタ型でないかもしれません。フィールド名を伴わないとき型名がフィールド名として扱われます。

// 匿名フィールド T1, *T2, P.T3, *P.T4を持つ構造体
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   で不整合
}

匿名フィールド内のフィールドとメソッド(§メソッドの宣言)は、その構造体直管のフィールドとメソッドへ昇格されます($セレクタ)。仮にS構造体とT型があるとすると、以下のルールが成り立ちます。

  • Sが匿名フィールドとしてTを有していれば、Sのメソッド群はTのメソッド群を含みます。
  • Sが匿名フィールドとして*Tを有していれば、Sのメソッド群は*Tのメソッド群(Tのメソッド群も含む)を含みます。
  • Sが匿名フィールドとしてTまたは*Tを有していれば、*Sのメソッド群は*Tのメソッド群(Tのメソッド群も含む)を含みます。

フィールドの宣言には、オプションで文字列リテラルを使ってタグを指定することができます。タグはそれが記述されているフィールド宣言の全フィールドの属性となります。タグはリフレクションインタフェースを使って参照できますが、それ以外は無視されます。

// TimeStampプロトコルバッファと一致した構造体
// タグの文字列でプロトコルバッファのフィールド番号を定義
struct {
	microsec  uint64 "field 1";
	serverIP6 uint64 "field 2";
	process   string "field 3";
}

ポインタ型

ポインタ型は所定の型(ベース型)の変数へのすべてのポインタの集合を表します。ポインタの値はnilをとることがあります。

PointerType = "*" BaseType .
BaseType = Type .
*int
*map[string] *chan int

関数型

関数型は、同一のパラメータと同一の戻り値を持つすべての関数の集合を表します。関数型の値はnilをとることがあります。

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

名前(IdentifierList)はパラメータリストまたは戻り値リストすべてに記述するか、またはすべて省略するかいずれかです。記述した場合、それぞれの名前がアイテム(パラメータまたは戻り値)ひとつを表します。省略した場合、それぞれの型がその型のアイテムひとつを表します。パラメータと結果リストは常に括弧でくくられます。例外として、戻り値がひとつだけで、名前がなく、括弧なしで記述された関数型でないときだけは括弧は不要です。

最後のパラメータだけは型の代わりに…と記述可能です。これは、この関数がゼロ個以上の任意の型である特別な引数を処理することを表します。

func ()
func (x int)
func () int
func (string, float, ...)
func (a, b int, z float) bool
func (a, b int, z float) (bool)
func (a, b int, z float, opt ...) (success bool)
func (int, int, float) (float, *[]int)
func (n int) (func (p* T))

インタフェース型

インタフェース型はメソッド群を規定します。メソッド群はインタフェース名で呼ばれます。インタフェース型の変数には、そのインタフェースの「全スーパーセットのメソッド群を持っている型」の値を格納することができます。そのような型はこのインタフェースを実装していると言えます。インタフェース型の値はnilをとることがあります。

InterfaceType      = "interface" "{" [ MethodSpecList ] "}" .
MethodSpecList     = MethodSpec { ";" MethodSpec } [ ";" ] .
MethodSpec         = MethodName Signature | InterfaceTypeName .
MethodName         = identifier .
InterfaceTypeName  = TypeName .

他のメソッド群でも同じですが、インタフェース型においてメソッドはユニークな名前を持つ必要があります。

// 単純なFileインタフェース
interface {
	Read(b Buffer) bool;
	Write(b Buffer) bool;
	Close();
}

インタフェースは複数の型に実装することができます。例えば、S1S2 の2つの型が次のメソッド群を持つ場合、

func (p T) Read(b Buffer) bool { return ... }
func (p T) Write(b Buffer) bool { return ... }
func (p T) Close() { ... }

(TS1S2を表すとして)File インタフェースはS1S2の両方に実装されます。S1S2が他のメソッドを持つか共有していても関係ありません。

型は、その型のメソッド群の一部から構成されているインタフェースすべてを実装していることになります。したがって複数の異なるインタフェースを実装することもできます。例を挙げるなら、すべての型は次の空(empty)インタフェースを実装しています。

interface{}

同様に、下のインタフェースの記述方法をみてください。ここでは型の宣言内でLockという名のインタフェースを定義しています。

type Lock interface {
	Lock();
	Unlock();
}

S1S2がこれらのメソッドを実装した場合、

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

S1S2Fileインタフェースと同様にLockインタフェースも実装します。

インタフェースにはメソッドを記述する代わりに、別のインタフェース型を含むことができます。仮に Tというインタフェース名を記述したとすると、これは Tのメソッドを明示的に列挙したのと同じことになります。

type ReadWrite interface {
	Read(b Buffer) bool;
	Write(b Buffer) bool;
}

type File interface {
	ReadWrite;  // ReadWrite内のメソッドを列挙したことと同じ
	Lock;       // Lock内のメソッドを列挙したことと同じ
	Close();
}

マップ型

マップ型はある型(要素型)の要素の順序を持たない集合で、要素は別の型(キー型)のユニークなキーにより索引付けされます。マップ型の値はnilをとることがあります。

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

キーどうしの比較に使用するため、キー型においては比較演算子==!=比較演算子)が完全に実装されている必要があります。ゆえにキー型は論理値型、数値型、文字列型、ポインタ型、関数型、インタフェース型、マップ型、チャネル型である必要があります。キー型がインタフェース型のとき、比較演算子は動的なキー値を比較するために完全に定義されていなければなりません。比較できなければランタイムエラーとなります。

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

要素の数は長さ(length)と呼ばれます。この値はマイナス値には成り得ません。マップmの長さは、組み込み関数len(m)を使用して調べることができ、コンパイル時に定数となります。マップ内の要素は、特殊な代入方法で実行中に追加、削除できます。

初期化されていないマップの値はnilです。空の新しいマップを作るには組み込み関数makeを使用します。make関数は引数にマップの型と、オプションでキャパシティのヒントをとります。

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

キャパシティの初期値は指定したサイズを超えることはありません。マップは格納する項目が収まるようにサイズを広げます。

チャネル型

チャネルは同時に実行されるふたつの関数に、同期実行と特定の要素型の値を受け渡す通信機構を提供します。チャネル型の値はnilをとることがあります。

ChannelType   = Channel | SendChannel | RecvChannel .
Channel       = "chan" ElementType .
SendChannel   = "chan" "<-" ElementType .
RecvChannel   = "<-" "chan" ElementType .

作成と同時にチャネルは送受信できるようになります。変換または代入によってチャネルは送信のみ、または受信のみ行うよう強制することができます。この制約はチャネルの方向と呼ばれ、送信のみ、受信のみ、双方向(制約なし)のいずれかとなります。

chan T         // T型の値を送受信可能
chan<- float   // floatの送信のみ
<-chan int     // intの受信のみ

初期化されていないチャネルの値はnilです。初期化済みの新しいチャネルを作るには組み込み関数makeを使用します。make関数は引数にチャネルの型と、オプションでキャパシティをとります。

make(chan int, 100)

キャパシティは要素数であり、チャネルのバッファサイズを指定します。キャパシティがゼロより大きいとき、チャネルは非同期になり、バッファがいっぱいになるまで、送信はブロックすることなく成功します。キャパシティがゼロまたは指定しなかったときは、通信は送信・受信側双方が準備ができているときだけ成功します。

組み込み関数closeを使ってチャネルのクローズ、およびclosedを使ってクローズされているか確認することができます。

型と値の特性

2つの型があるとします。これらは同じ型であったり、異なる型であったり、互換性を持つ型どうしだったり、互換性を持たなかったりします。同じ型どうしは常に互換性を持っていますが、2つの型が互換性を持つからといって同一の型であるとは限りません。

型の同一性と互換性

型の同一性

2つの型の名前が同一の宣言によるものなら、これら2つの型は同一です(§宣言とスコープ)。名前を持つ型と、無名の型が同一の型であることはあり得ません。2つの無名の型が、対応する型リテラルが同じリテラル構造を持ち、かつ対応する構成要素が同一の型であれば、この2つは同一の型です。詳しく説明します。

  • 配列型の場合は、要素の型と配列の長さが同じであれば同一の型です。
  • スライス型の場合は、要素の型が同じであれば同一の型です。
  • 構造体型の場合は、フィールドの並び順と、対応するフィールドの名前と型が同じであれば同一の型です。このとき匿名フィールドどうしは同じ名前であるとみなされます。
  • ポインタ型の場合は、ベース型が同じであれば同一の型です。
  • 関数型の場合は、パラメータと戻り値の個数、および対応するパラメータと戻り値の型がそれぞれ同じであれば同一の型です。“…”パラメータはすべて同一の型として規定されています。パラメータと戻り値の名前は比較には使われません。
  • インタフェース型の場合は、同じ名前、同じ関数型のメソッド群を持っていれば同一の型です。メソッドの順序は関係ありません。
  • マップ型の場合は、キーと値の型が同じであれば同一の型です。
  • チャネル型の場合は、値の型と方向が同じであれば同一の型です。

型の互換性

型の互換性は、同一性ほどは厳密ではありません。型の名前の有り無しに関わらず、お互いの型リテラルが互換性を持ってさえいれば、2つの型には互換性があります。他の点において、型の互換性の定義は先ほど解説した型の同一性と同じなので、「同一」を「互換性を持つ」に置き換えてみてください。

宣言をおこないます。

type (
	T0 []string;
	T1 []string;
	T2 struct { a, b int };
	T3 struct { a, c int };
	T4 func (int, float) *T0;
	T5 func (x int, y float) *[]string;
)

次の型どうしは同一です。

T0 と T0
[]int と []int
struct { a, b *T5 } と struct { a, b *T5 }
func (x int, y float) *[]string と func (int, float) (result *[]string)

T0T1は同一でもなければ、互換性もありません。それは、これらが別個に宣言された名前付きの型であるためです。

次の型どうしは互換性があります。

T0 と T0
T0 と []string
T3 と struct { a int; c int }
T4 と func (x int, y float) *[]string

T2struct { a, c int }には互換性はありません。それはフィールド名が異なるためです。

代入の適合性

下の条件をひとつ以上満たすとき、静的な型Vの値であるvは、型Tに代入できます。

  • VTに互換性がある。
  • Tはインタフェース型であり、VT実装している。
  • Vは配列のポインタで、Tはそれと互換性のある要素型を持ったスライスであり、なおかつVTどちらかが名前を持たない。(代入後、スライスの変数は配列を参照するようになります。要素はコピーされるわけではありません。)
  • Vは双方向チャネルで、Tはそれと互換性のある要素型を持ったチャネルであり、すくなくともVTどちらかが名前を持たない。

Tが構造体のときは、Tの全フィールドがエクスポートされているか、またはTが宣言されているパッケージ内で代入が行われる必要があります。言い換えると構造体型の値を構造体型の変数に代入できるのは、構造体の全フィールドが個別に代入可能なときだけです。

型を持たない定数vが、型Tの値を正しく表すことができるならば、vは型Tに対し代入の適合性を持ちます。

事前宣言済み識別子nilは、ポインタ・関数・スライス・マップ・チャネル・インタフェース型いずれにも代入の適合性を持ち、それらの型のゼロ値を表します。

また、ブランク識別子にはどんな値でも代入可能です。

比較の適合性

あるケースを除き、すべての型は、互換性がある他の静的な型と値を比較することができます。数値型と文字列型の値は、全種類の比較演算子を使って比較することができますが、論理値型はイコールかそうでないかの比較のみできます。

コンポジット型の値は演算子==!=を使用して、イコールかそうでないか比較します。以下に但し書きがあります。

  • 配列および構造体は、どれとも比較できません。
  • スライスの値はnilとだけ比較できます。スライスの値がnilとなるのは、明示的にnilを代入したか、初期化されていないか、nil値を持つ他のスライスの値を代入したときだけです。
  • インタフェースの値がnilとイコールになるのは、nilを明示的に代入したか、初期化されていないか、nil値を持つ他のインタフェースの値を代入したときです。
  • nilと比較可能な型どうしは、2つの値がともにnilであればイコールであり、一方だけがnilであればイコールではありません。
  • ポインタの値は、双方が同じ位置を指し示していればイコールです。
  • 関数の値は、双方が同じ関数を参照していればイコールです。
  • チャネルとマップの値は、双方が同一のmakeの呼び出して作成されたときにイコールとなります(§スライス、マップ、チャネルの作成)。2つのチャネル型の値を比較する際、チャネルの型どうしが互換性を持っている必要がありますが、チャネルの方向は無視されます。
  • インタフェースの値は、双方の静的な型が互換性を持っていれば比較できます。イコールとなるのは双方が同じ動的な型をもち、かつその値自体がイコールのときだけです。

ブロック

ブロックは、対になる波括弧{}内の一連の宣言とステートメントです。

Block = "{" StatementList "}" .

ソースコード内には明示的なブロックの他に、下に示す潜在的なブロックがあります。

  1. すべてのGo言語ソースを包括する、ユニバースブロック。
  2. パッケージ内の全Go言語ソースを包括する、パッケージブロック。
  3. 各ファイル内の全Go言語ソースを包括する、ファイルブロック。
  4. ifforswitchステートメントは、それ自身が存在的なブロックであるとみなされます。
  5. switchselectステートメント内の各条項は、存在的なブロックの働きをします。

ブロックはネストし、スコープに影響します。

宣言とスコープ

宣言をおこなうことによって識別子(ブランク識別子を除く)は、定数、型、変数、関数、パッケージにバインドされます。プログラム内のすべての識別子は、宣言されなければなりません。識別子は同じブロック内で2回宣言されることはありません、また識別子をファイルブロックとパッケージブロックの両方で宣言することはできません。

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

スコープとは、ソース内で宣言済み識別子に割り当てられた定数、型、変数、関数、パッケージを参照可能な範囲のことです。

Go言語ではブロックを使ってスコープ制御を行います。

  1. 事前宣言済み識別子が持つスコープは、ユニバースブロックです。
  2. トップレベル(関数の外)にて宣言されている定数、型、変数、関数の識別子が持つスコープは、パッケージブロックです。
  3. インポートされたパッケージの識別子が持つスコープは、そのimport宣言を含んでいるファイルブロックです。
  4. 関数のパラメータまたは戻り値として宣言されている変数の識別子が持つスコープは、関数の本体部分です。
  5. 関数のなかで宣言されている定数または変数の識別子が持つスコープは、ConstSpecまたはVarSpecの直後から始まり、その識別子を含んでいるブロックの終わりまでです。
  6. 関数内で宣言した型の識別子が持つスコープは、TypeSpecの識別子から始まり、その識別子を含んでいるブロックの終わりまでです。

ブロック内で宣言された識別子は、内側のブロック内で再宣言されます。ブロック内で宣言した識別子がスコープ内にある間、その識別子は宣言したときの実体を表しつづけます。

パッケージ節は宣言ではありません。パッケージ名がどのスコープにも現れることがないからです。パッケージ節は同一のパッケージに所属するファイルを識別するためと、インポートされるときにデフォルトとして使われるパッケージ名を定義するためのものです。

ラベルのスコープ

ラベルはラベルステートメントによって宣言され、breakcontinuegotoステートメント内で使用されます(§Break ステートメント、§Continue ステートメント、§Goto ステートメント)。ラベルはその他の識別子と異なり、ブロックによるスコープ制御は行われず、ラベル以外の識別子との重複が許されます。ラベルのスコープは、そのラベルが宣言されている関数の本体内ですが、ネストした関数はスコープ外です。

事前宣言済み識別子

下に示す識別子は、ユニバースブロック内で明示的に宣言されています。

基本型:
	bool byte float32 float64 int8 int16 int32 int64
	string uint8 uint16 uint32 uint64

アーキテクチャ仕様に従う型:
	float int uint uintptr

定数:
	true false iota

ゼロ値:
	nil

関数:
	cap close closed len make new panic panicln print println

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

識別子は、他パッケージからの限定付き識別子を使ったアクセスを許可するためにエクポートされることがあります。識別子は下の2つの条件を満たすときエクスポートされます。

  1. 識別子の名前の最初の一文字が大文字(ユニコードクラス”Lu”)である。なおかつ
  2. 識別子がパッケージブロック内で宣言されている、またはパッケージブロック内で宣言された型のフィールドかメソッドである。

それ以外の識別子がエクスポートされることはありません。

ブランク識別子

アンダースコア(_)を使って記述されるブランク識別子は、他の識別子と同じく宣言にて使用されますが、その宣言でバインドは行われません。

定数の宣言

定数の宣言では、識別子(定数名)のカンマ区切りのリストを、定数式のリストにバインドします。識別子と式の数は一致している必要があり、左辺のn番目の識別子が、右辺のn番目の式にバインドされます。

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

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

定数の宣言時に型を指定したときは、定数はその型になり、式はその型に対して代入の適合性を持たなくてはならなくなります。型を省略したときは、型と対応する式によってそれぞれの型が決まります。式の値が型を持たない定数のときは、宣言された定数の識別子も同様に型を持たない定数値を表します。たとえば式が浮動小数点リテラルのとき、たとえ小数部の値が0であっても、識別子は浮動小数点の定数を表します。

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 float = 0, 3      // u = 0.0, v = 3.0

括弧でくくられたconst宣言リスト内では、式リストは最初の宣言以外では省略可能です。このように式リストが省略されたときは、直近で使用した式リスト(型が指定されていれば型も)が適用されます。したがって、式リストを省略することは前の式リストを繰り返すことと同じになります。識別子の数は、直前の式リスト内の式の数と一致していなければなりません。iota定数ジェネレータを使うことで、シーケンシャルな値の宣言が簡単に行えるようになります。

const (
	Sunday = iota;
	Monday;
	Tuesday;
	Wednesday;
	Thursday;
	Friday;
	Partyday;
	numberOfDays;  // この定数はエクスポートされない
)

Iota

定数の宣言で使用される事前宣言済み擬似定数iotaは、連続した型を持たない整数定数値を生成します。予約語constがソース内に現れると値が0にリセットされ、セミコロンが現れると値がひとつ増やされます。これは関連し合う定数グループを作成するときに利用されます。

const (  // iotaは0にリセットされる
	c0 = iota;  // c0 == 0
	c1 = iota;  // c1 == 1
	c2 = iota   // c2 == 2
)

const (
	a = 1 << iota;  // a == 1 (iotaはリセット済み)
	b = 1 << iota;  // b == 2
	c = 1 << iota;  // c == 4
)

const (
	u       = iota * 42;  // u == 0     (型を持たない整数定数)
	v float = iota * 42;  // v == 42.0  (浮動小数点定数)
	w       = iota * 42;  // w == 84    (型を持たない整数定数)
)

const x = iota;  // x == 0 (iotaはリセット済み)
const y = iota;  // y == 0 (iotaはリセット済み)

ひとつの式リスト内では、iotaの値は常に同じです。これはiotaの値はセミコロンが現れた時だけ増やされるためです。

const (
	bit0, mask0 = 1 << iota, 1 << iota - 1;  // bit0 == 1, mask0 == 0
	bit1, mask1;                             // bit1 == 2, mask1 == 1
	_, _;                                    // スキップ iota == 2
	bit3, mask3;                             // bit3 == 8, mask3 == 7
)

上の例は、直前の式リストを繰り返し適用し続けています。

型の宣言

型の宣言は型名となる識別子を、元になる型と同じ定義(エレメント、フィールド、チャネルの方向など)を持った新しい型としてバインドします。
また新しい型は、元になった型とは互換性がありますが異なる型です。

TypeDecl     = "type" ( TypeSpec | "(" [ TypeSpecList ] ")" ) .
TypeSpecList = TypeSpec { ";" TypeSpec } [ ";" ] .
TypeSpec     = identifier Type .
type IntArray [16]int

type (
	Point struct { x, y float };
	Polar Point
)

type TreeNode struct {
	left, right *TreeNode;
	value *Comparable;
}

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

宣言された型は、バインドした型からはメソッドを継承しません。しかしコンポジット型の要素が持っているメソッド群はそのまま残ります。

// MutexはLockとUnlockメソッドを持つデータ型
type Mutex struct         { /* Mutex fields */ }
func (m *Mutex) Lock()    { /* Lock implementation */ }
func (m *Mutex) Unlock()  { /* Unlock implementation */ }

// NewMutexはMutexと同じ構成ではあるが、メソッド群は空である
type NewMutex Mutex

// PrintableMutexのメソッド群には、匿名フィールドMutexに
// バインドされているLockとUnlockメソッドが含まれる
type PrintableMutex struct {
	Mutex;
}

論理値型、数値型、文字列型から別の型を定義してメソッドを追加するために、型の宣言が使われることがあります。

type TimeZone int

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

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

変数の宣言

変数は宣言によって作成、バインドされ、型と必要であれば初期値を与えられます。

VarDecl     = "var" ( VarSpec | "(" [ VarSpecList ] ")" ) .
VarSpecList = VarSpec { ";" VarSpec } [ ";" ] .
VarSpec     = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
var i int
var U, V, W float
var k = 0
var x, y float = -1, -2
var (
	i int;
	u, v, s = 2.0, 3.0, "bar"
)
var re, im = complexSqrt(-1)
var _, found = entries[name];  // マップからの引当。存在するかどうかだけ知りたい

式リストが与えられたときは、変数にはその式が順番に代入 (§代入)され、初期化されます。そのときは、式リスト内のすべての式が代入に使用され、全ての変数がそれによって初期化される必要があります。式リストが与えられなければ、各変数はゼロ値で初期化されます。

型が指定されたときは、変数はその型になります。指定されないときは、代入された式から型が推測されます。

型が指定されず、かつ対応する式が型を持たない定数のときは、宣言された変数の型は、boolintfloatstringのいずれかになります。どれになるかは定数の値に依存します。

var b = true    // tはbool型
var i = 0       // iはint型
var f = 3.0     // fはfloat型
var s = "OMDB"  // sはstring型

省略形式による変数の宣言

省略形式による変数の宣言には、下の文法が使われます。

ShortVarDecl = IdentifierList ":=" ExpressionList .

これは変数の宣言の省略形式で、初期化式は伴いますが型の指定は行いません。

"var" IdentifierList = ExpressionList .
i, j := 0, 10;
f := func() int { return 7; }
ch := make(chan int);
r, w := os.Pipe(fd);  // os.Pipe()は戻り値を2つ返す
_, y, _ := coord(p);  // coord()は戻り値を3つ返すが、y座標だけ使用する

非省略形式とは異なり、この省略形式による変数の宣言は、同じブロック内で、同じ型として宣言された変数に対して再宣言が可能です。ただし、このとき宣言する変数のうち少なくともひとつは、ブランク変数ではない新しい変数である必要があります。結果として、再宣言は複数の値に対してのみ適用できます。再宣言では新しい変数を作成するのではなく、単に変数に新しい値を割り当てます。

field1, offset := nextField(str, 0);
field2, offset := nextField(str, offset);  // offsetの再宣言

省略形式は関数内でのみ使用されます。また省略形式はifforswitchの初期化コンテキストにおいて、一時的なローカル変数を宣言するために用いられます(§ステートメント)。

関数の宣言

関数の宣言は、識別子と関数をバインドします(§関数型)。

FunctionDecl = "func" identifier Signature [ Body ] .
Body         = Block.

関数の宣言の際、関数の本体部分を省略することが可能です。このような宣言はアセンブリルーチンのようなGo言語の外で実装された関数へのシグネチャを定義します。

func min(x int, y int) int {
	if x < y {
		return x;
	}
	return y;
}

func flushICache(begin, end uintptr)  // 外部にて実装

メソッドの宣言

メソッドの宣言は、識別子とメソッド(レシーバを伴った関数)をバインドします。

MethodDecl   = "func" Receiver MethodName Signature [ Body ] .
Receiver     = "(" [ identifier ] [ "*" ] BaseTypeName ")" .
BaseTypeName = identifier .

Tが型の名前とすると、レシーバの型はTまたは*Tの形式でなければなりません。Tはレシーバベース型、または単にベース型と呼ばれます。このベース型はポインタまたはインタフェース型であってはならず、またメソッドと同じパッケージ内で宣言されていなくてはなりません。メソッドの宣言は、「メソッドをベース型にバインドする」とも言われます。メソッドはセレクタを使ってその型と一緒に指定したときのみ参照可能になります (§型の宣言、§セレクタ)。

Point型にメソッドの宣言を行います。

func (p *Point) Length() float {
	return Math.sqrt(p.x * p.x + p.y * p.y);
}

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

レシーバ型が*PointLengthメソッドとScaleメソッドを、ベース型Pointにバインドしています。

レシーバの値がメソッドの本体部で参照されないのであれば、宣言時にレシーバの識別子を省略可能です。同じことは、一般に関数とメソッドのパラメータにもあてはまります。

メソッドの型は、第一引数にレシーバを伴った関数の型となります。例えばScaleメソッドは次の型になります。

(p *Point, factor float)

しかし、このように宣言しても関数はメソッドにはなりません。

式は、値の算出方法を規定します。値の算出はオペランドに演算子および関数を適用することで行われます。

オペランド

オペランドは、式の基本要素である値です。

Operand    = Literal | QualifiedIdent | MethodExpr | "(" Expression ")" .
Literal    = BasicLit | CompositeLit | FunctionLit .
BasicLit   = int_lit | float_lit | char_lit | StringLit .

限定付き識別子

限定付き識別子とは、パッケージ名をプレフィックスとして指定した識別子で、これにはブランク識別子は使用できません。

QualifiedIdent = [ PackageName "." ] identifier .

限定付き識別子は、別パッケージの識別子にアクセスするときに使用します。その識別子はエクスポートされていなければなりません。すなわち識別子がユニコードの大文字で始まっている必要があります。

math.Sin

複合リテラル

複合リテラルは構造体、配列、スライス、マップを構築し、評価をその都度行って新しい値を作成します。複合リテラルは、値の型と、それに続く波括弧{}でくくられた要素リストから構成されます。この要素は単一式、もしくはキーと値のペアのどちらかです。

CompositeLit  = LiteralType "{" [ ElementList ] "}" .
LiteralType   = StructType | ArrayType | "[" "..." "]" ElementType |
                SliceType | MapType | TypeName | "(" LiteralType ")" .
ElementList   = Element { "," Element } [ "," ] .
Element       = [ Key ":" ] Value .
Key           = FieldName | ElementIndex .
FieldName     = identifier .
ElementIndex  = Expression .
Value         = Expression .

このLiteralTypeは、構造体、配列、スライス、マップ型のいずれかでなければなりません(文法上、型がTypeNameと記述されたとき以外はこの制約が適用されます)。式の型は、LiteralTypeの各フィールド、または要素、またはキーの型との間で代入の適合性を持たなければなりません。このとき変換はできません。
キーは、構造体リテラルのフィールド名、配列またはスライスリテラルのインデックス式、マップリテラルのキーのいずれかとして解釈されます。マップリテラルのときは、全ての要素に対しキーを記述しなくてはなりません。また複数の要素に同じフィールド名やキー値を指定したときはエラーとなります。

構造体リテラルには次の規則が適用されます。

  • キーはLiteralTypeで宣言されているフィールド名でなければなりません。
  • リテラルにキーが含まれないときは、各フィールドの要素をフィールドが宣言されている順にリストしなければなりません。
  • キーを持つ要素がひとつでもあるなら、全ての要素にキーを持たせなければなりません。
  • リテラルにキーが含まれているときは、構造体の全フィールドに要素を持たせる必要はありません。省略されたフィールドはゼロ値となります。
  • リテラルの要素リストは省略可能です。このようなリテラルはその型のゼロ値となります。
  • 他のパッケージに属している構造体の非エクスポートフィールドに要素を設定しようとするとエラーとなります。

構造体を定義します。

type Point struct { x, y, z float }
type Line struct { p, q Point }

次のように記述します。

origin := Point{};                            // Pointはゼロ値
line := Line{origin, Point{y: -4, z: 12.3}};  // line.q.xはゼロ値

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

  • 各要素は、配列内の位置を示す整数インデックスを持ちます。
  • キーを伴った要素は、そのキーをインデックスとして使用します。キーは整数の定数式でなければなりません。
  • キーを伴わない要素は、前の要素のインデックスを+1した値をインデックスとして用います。先頭の要素がキーを伴わないときは、そのインデックスはゼロです。

複合リテラルのアドレスを取得(§アドレス演算子)すると、リテラル値のインスタンスを指すユニークなポインタが生成されます。

var pointer *Point = &Point{y: 1000};

配列リテラルの長さは、LiteralType内で指定した長さです。要素数がリテラルで指定した長さに足りないとき、不足した要素にはその要素型のゼロ値がセットされます。配列のインデックスの範囲を超えたインデックス値を要素に指定するとエラーとなります。配列の長さに…と記述すると、要素の最大インデックス値に+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}

そして次は、配列リテラルに対してスライス操作を行うショートカットです。

[n]T{x1, x2, ... xn}[0 : n]

“if”、”for”、”switch”ステートメントの条件内に、LiteralTypeとしてTypeName形式を使った複合リテラルが現れると、意味が曖昧になり構文解析に支障をきたします。これはリテラル内の波括弧{}でくくられた式と、これらのステートメントに続くステートメントブロックとの見分けがつかないためです。この稀なケースにて発生する曖昧さを解決するには、複合リテラルを丸括弧()内に記述しなければなりません。

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

次は正しく配列、スライス、マップリテラルを使った例です。

// 素数リスト
primes := []int{2, 3, 5, 7, 9, 11, 13, 17, 19, 991};

// chが母音(vowel)のとき、vowels[ch]の値はtruevowels[ch]
vowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true};

// 配列 [10]float{-1, 0, 0, 0, -0.1, -0.1, 0, 0, 0, -1};
filter := [10]float{-1, 4: -0.1, -0.1, 9: -1};

// 平均律音階の周波数(Hz) (A4 = 440Hz)
noteFrequency := map[string]float{
	"C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83,
	"G0": 24.50, "A0": 27.50, "B0": 30.87,
}

関数リテラル

関数リテラルは匿名関数を表します。匿名関数は関数の型および関数の本体から構成されます。

FunctionLit = FunctionType Body .
func (a, b int, z float) bool { return a*b < int(z) }

関数リテラルは変数に代入することも、直接実行することも可能です。

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

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

基本式

基本式は、単項式、二項式に与えられるオペランドです。

PrimaryExpr =
	Operand |
	Conversion |
	BuiltinCall |
	PrimaryExpr Selector |
	PrimaryExpr Index |
	PrimaryExpr Slice |
	PrimaryExpr TypeAssertion |
	PrimaryExpr Call .

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

セレクタ

次の形式の基本式があります。

x.f

これは、x(またはxがポインタ型であれば*x)で表される値が持つ、フィールドまたはメソッドfを表します。識別子fは(フィールドまたはメソッドの)セレクタと呼ばれます。セレクタはブランク識別子であってはなりません。この式の型はfの型です。

セレクタfは、型Tのフィールド/メソッドfを表すか、もしくはT内でネストしている匿名フィールドのフィールド/メソッドfを表します。fに到達するまで渡り歩いた匿名フィールドの数は、Tにおけるfの深さと呼ばれます。Tに直接宣言されていればフィールド/メソッドfの深さはゼロです。T内の匿名フィールドAで宣言されていればフィールド/メソッドfの深さは、Aの深さ+1となります。

セレクタには次の規則が適用されます。

  1. 仮に、型Tもしくは*Tである値xがあり、Tがインタフェース型でなく、該当するフィールドまたはメソッドfが存在するならば、x.fが表すのは、T内で深さの値が最も小さいフィールドまたはメソッドfです。最も浅い深さのfがひとつだけでなければ、このセレクタは不正となります。
  2. 仮に、型Iもしくは*Iである値xがあり、Iがインタフェース型で、かつ該当するメソッドが存在するならば、x.fが表すのは、xに割り当てられている値が持っているfという名前の実メソッドです。xに値がないかnilのとき、x.fは不正となります。
  3. これら以外のケースでは、x.fは不正となります。

セレクタは自動的にポインタの間接参照を行います。xがポインタ型のとき、x.y(*x).yの簡略形として使用可能です。yも同じくポインタ型のとき、x.y.zも同様に(*(*x).y).zの簡略形です。
ただし、*xがポインタ型のときは、明示的に間接参照を行わなければなりません。これは自動間接参照が行われるのは1レベルだけだからです。例えば、T型の値x*Aとして宣言された匿名フィールドを含んでいるとき、x.f(*x.A).fの簡略形です。

例文のために、宣言を行います。

type T0 struct {
	x int;
}

func (recv *T0) M0()

type T1 struct {
	y int;
}

func (recv T1) M1()

type T2 struct {
	z int;
	T1;
	*T0;
}

func (recv *T2) M2()

var p *T2;  // with p != nil and p.T1 != nil

次のように記述します。

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

p.M2        // (*p).M2
p.M1        // ((*p).T1).M1
p.M0        // ((*p).T0).M0

インデックス

次の形式の基本式があります。

a[x]

これは、配列、スライス、文字列、マップa内の、xでインデックス指定された要素を表します。このxの値は、インデックスまたはマップのキーと呼ばれます。これには次の規則が適用されます。

仮にaが、配列型の型Aまたは*A、もしくはスライス型の型Sの値であるとします。

  • xは整数値で、0 <= x < len(a)でなければならない
  • a[x]はインデックスxの位置にある配列要素であり、a[x]の型はAの要素型である

仮にaが、文字列型である型Tの値であるとします。

  • xは整数値で、0 <= x < len(a)でなければならない
  • a[x]はインデックスxの位置にあるバイトであり、a[x]の型はbyte型である
  • a[x]には値を代入できない

仮にaが、マップ型である型Mの値であるとします。

  • xの型は、Mのキーの型と互換性を持つ必要があり、かつこのマップはxをキーとするエントリを持っていなければならない
  • a[x]は、マップ内のxをキーとする値であり、a[x]の型はMの値の型である

これ以外のa[x]は不正となります。また、インデックスまたはキーが範囲外であるときは、たとえインデックス式としては正しくても、ランタイム例外が発生します。

ただし、インデックス式がマップであるときは、次の形式を使ってmap[K] V型であるマップaから代入または、変数の初期化を行えます。

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

このインデックス式の結果として、(V, bool)型の2つの値が返されます。指定したキーがマップ内に存在したときは、この式は(a[x], true)を返します。存在しない時は、(Z, false)を返します。このZV型のゼロ値です。このときは、ランタイム例外は発生しません。上の例のように、このときのインデックス式は、値と成否を返すような関数を呼び出したのと同様に振舞います。 (§代入)

同様に、マップに代入するときは、次の特殊な形式を使うことができます。

a[x] = r, ok

このとき、論理値であるokの値がfalseであれば、キーがxであるエントリはマップから削除されます。okの値がtrueであれば、通常通りマップに要素が代入されます。

スライス

文字列、配列、またはスライス自身をスライスすることで、部分文字列、または部分配列の参照を作ることができます。スライスを行うときは、結果として得たい要素をインデックス式で選択します。得られる結果は0から始まるインデックスを持ち、長さはスライスするときに指定した2つのインデックス値の差と等しくなります。次は配列aのスライスです。

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

このスライスsの型は[]intであり、長さは2、キャパシティは3です。要素の値は次となります。

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

スライスの長さはマイナス値にはなりません。また配列または文字列のスライス作成時のインデックス[lo:hi]が、「 0 <= lo <= hi <= 長さ」を満たしていなくてはなりません。スライスからスライスを作成するときは、上限値は長さではなくキャパシティとなります。

スライスする対象が、文字列またはスライスのとき、スライスの結果は文字列もしくは同じ型のスライスになります。しかしスライスの対象が配列のときは、スライスの結果は、その配列の要素型と同じ要素型を持ったスライスになります。

型アサーション

xおよび型Tがあると仮定して、次の基本式をみてください。

x.(T)

この式は、xはゼロ値ではなく、かつxにはT型の値が格納されていると断定します。このx.(T)という表記は、型アサーションと呼ばれます。このときのxの型はインタフェース型でなければなりません。

より明確にすると、Tがインタフェース型でないときx.(T)は、xの動的な型とTが同一の型であることを表しています。(§型の同一性と互換性)。もし、Tがインタフェース型のときx.(T)は、xがインタフェースTを実装している動的な型であることを表します(§インタフェース型)。

型アサーションが有効であれば、その式が表す値は、xに格納されている型Tの値となります。ただし型アサーションに失敗したときはランタイム例外が発生します。これらを言い換えると、正しいプログラムにおいては、xの動的な型が実行時にしか分からなくともx.(T)Tになりうることだけは分かっているということです。

型アサーションが代入、または初期化で使われるときは次の形式になります。

v, ok = x.(T)
v, ok := x.(T)
var v, ok = x.(T)

このアサーションの結果として、(T, bool)型の2つの値が返されます。アサーションに成功したときは、この式は(x.(T), true)を返します。失敗したときはこの式は(Z, false)を返します。このZT型のゼロ値です。このときは、ランタイム例外は発生しません。上の例のように、このときの型アサーションは、値と成否を返すような関数を呼び出したのと同様に振舞います。 (§代入)

呼び出し

下は、F型の関数であるfの式です。

f(a1, a2, ... an)

これは引数、a1, a2, ... anを伴なうfの呼び出しです。1つの特例を除いて、各引数は単一値となる式であり、その値はFのパラメータの型と代入の適合性を持つ必要があります。これらの引数の式は関数の呼び出し前に評価されます。この式の型は、Fの戻り値の型となります。メソッドの実行も同様ではありますが、メソッドは、そのメソッドのレシーバの型の値に対するセレクタとして指定されます。

Atan2(x, y)    // 関数の呼び出し
var pt *Point;
pt.Scale(3.5)  // レシーバptによるメソッドの呼び出し

特例として、関数またはメソッドgの戻り値と、別の関数またはメソッドであるfの各パラメータの数およびそれらの代入の適合性が一致していれば、f(g(parameters_of_g))の呼び出しによって、gの戻り値をfのパラメータとして順に代入したのち、fが実行されます。ただし、fの呼び出しにはgからの戻り値以外のパラメータを指定することはできません。またfの最後のパラメータが…のときは、fの戻り値のうち通常の代入を行った残りがそこに代入されます。

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

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

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

メソッド呼び出しx.m()は、x(の型)のメソッド群がmを含んでいて、かつ引数リストがmの引数リストと代入の適合性があるときに有効となります。またxアドレス指定可能であり、&xのメソッド群がmを含んでいるならば、x.m()は、(&x).m()の簡略形として使用可能です。

var p Point;
p.Scale(3.5)

これ以外のメソッド型やメソッドリテラルはありません。

…パラメータの解析

関数fが…パラメータを持つとき、…は常に一番最後の仮パラメータとなります。fの呼び出しのとき、…より前の引数は通常通り扱われます。それらのパラメータのあとに続いて現れた任意数(ゼロも含む)の引数が…パラメータにバインドされます。

f関数内では…パラメータは、静的な型interface{} (空インタフェース)を持ちます。
各呼び出しにおいて、このパラメータの動的な型は、呼び出し時に並べられた引数を連続したフィールドとして持つ構造体となります。つまり、…に与えられた実引数が構造体にラップされて、実引数の代わりとして渡されます。リフレクションインタフェースを使用すると、この動的な型から要素を取り出して、本来の実引数を得ることができます。

関数とその呼び出しです。

func Fprintf(f io.Writer, format string, args ...)
Fprintf(os.Stdout, "%s %d", "hello", 23);

このFprintf呼び出しにおいて、このargsの動的な型は概念的に、struct { string; int }となります。

特例として、関数が受け取った…パラメータを、別の関数を呼び出す際に…パラメータとして使用するときは、このパラメータは再ラップされることなくそのまま渡されます。すなわち、…仮パラメータは変更されることなく…実パラメータとして受け渡されます。

演算子

演算子はオペランドを伴って式を作ります。

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

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

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

比較演算子については別途説明します。それ以外の二項演算子では、チャネル、シフト、型を持たない定数のいずれかを伴う演算子を除き、オペランドの型は同じでなければなりません (§型と値の特性)。定数のみを伴う演算子については、定数式のセクションを参照ください。

チャネルの送信のときは、最初のオペランドは常にチャネルであり、2番目のオペランドは、チャネルの要素型に対して代入の適合性を持つ値でなくてはなりません。

シフト演算を除き、一方のオペランドが型を持たない定数で、もう一方がそれ以外のときは、定数のオペランドが相手側のオペランドの型に変換されます。

シフト演算の右側のオペランドは符号なし整数型か、もしくは符号なし整数型に変換可能で型を持たない定数でなければなりません。

定数とならないシフト演算の場合で、左側オペランドが型を持たない定数であるときは、その定数の型はシフト演算自体を左側オペランドひとつと置き換えてみたときに得られる型となります。

var s uint = 33;
var i = 1<<s;          // 1はint型
var j = int32(1<<s);   // 1はint32型で、j == 0
var u = uint64(1<<s);  // 1はint64型で、u == 1<<33
var f = float(1<<s);   // 不正。1はfloat型で、シフト不可
var g = float(1<<33);  // 正しい。1<<33はシフト演算の定数で、g == 1<<33

演算子の優先順位

単項演算子は高い優先順位を持っています。++--演算子は、式ではなくステートメントを構成するため演算子のグループからは除外されています。そのためステートメント*p++は、(*p)++と同じです。

二項演算子には、6つの優先順位レベルがあります。乗算演算子は最も強く、それに続いて加算演算子、比較演算子、<- (チャネル送信)、&&(論理積)、最後が ||(論理和)です。

優先順位         演算子
    6             *  /  %  <<  >>  &  &^
    5             +  -  |  ^
    4             ==  !=  <  <=  >  >=
    3             <-
    2             &&
    1             ||

同じ優先順位を持つ二項演算子は、左から右へと対応づけされます。例で示すと、x / y * zは、(x / y) * zと同じになります。

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

算術演算子

算術演算子は数値に対して使用します。その算出結果の型は一つ目のオペランドの型と同じになります。四則演算子(+, -, *, /)は、整数及び浮動小数点に対して使用しますが、+は文字列にも使います。その他の算術演算子は整数にのみ使います。

+    和                     整数、浮動小数点、文字列
-    差                     整数、浮動小数点
*    積                     整数、浮動小数点
/    商                     整数、浮動小数点
%    剰余                   整数

&    ビット演算 and          整数
|    ビット演算 or           整数
^    ビット演算 xor          整数
&^   ビットクリア(and not)   整数

<<   左シフト                整数 << 符号なし整数
>>   右シフト                整数 >> 符号なし整数

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

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

文字列の加算は、オペランドを連結することで新たな文字列を作り出します。

整数型のとき、/%は以下の関係を満たします。

(a / b) * b + a % b == a

(a / b)は、ゼロに近づくように切り捨て/切り上げられます。例を上げますと、

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

被除数が正の値で、除数が2の累乗の定数であるときは、その割り算は右シフトに置き換えられ、剰余の計算はビット演算のANDに置き換えられます。

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

シフト演算子は、右オペランドで指定されたシフト数、左オペランドをシフトします。実装としては、シフトの左オペランドが、符号あり整数のとき算術シフト、符号なし整数のときは論理シフトが使われます。シフト数は符号なし整数でなければなりません。また、シフト数には上限がありません。シフト数nでシフトを行うとき、シフト演算は左オペランドをn回繰り返して、シフト数1でシフトしたように振舞います。シフト演算の結果として、x << 1x*2と同じであり、x >> 1x/2を負の無限大の値に近づくように切り捨てた値と同じになります。

整数オペランドに対する単項演算子+-^は以下で示すように定義されています。

+x                          は、0 + x
-x    符号反転               は、0 - x
^x    ビットの補数            は、m ^ x (xが符号なしのとき、mの全ビットは1。
                                      xが符号ありのとき、mは-1)

浮動小数点においては、+xxと同じであり、-xxの符号を反転させた値です。

整数のオーバフロー

符号なし整数において、+-*<<演算子はmodulo 2n (nは、この符号なし整数型のビット幅)で計算されます(§数値型)。大雑把な解説をすると、これら符号なし整数の演算は、オーバフローした高位のビットを破棄するので、プログラム側はこの「ラップアラウンド」が行われることを期待してもよいでしょう。

符号あり整数において、+-*<<演算子はオーバフローを起こすことがあり、演算結果の値として何が返されるかは、この符号あり整数の値、演算子、オペランドにより決まります。オーバフローが起きても例外は発生しません。オーバフローは起こらないという前提のため、コンパイラはこれに関するコードの最適化は行いません。たとえば、x < x + 1が常に成り立つとは限りません。

比較演算子

比較演算子は、bool型の値を返します。演算子==!=は、いくつかのケースを除いて配列と構造体以外のすべての型に適用できます。他の比較演算子に適用できるのは数値と文字列だけです。

==    等しい
!=    等しくない
<     小なり
<=    小なりイコール
>     大なり
>=    大なりイコール

数値型のオペランドは、一般的な方法で比較されます。

文字列型のオペランドは、バイト単位で(辞書的に)比較されます。

論理値型のオペランドは、双方がtrue 、または双方がfalseのときに等しいとみなされます。

複合型の比較の規則は、§比較の適合性にて解説します。

論理演算子

論理演算子は論理値に適用され、オペランドと同じ型で結果を返します。右オペランドが評価されるかどうかは条件によります。

&&    and条件    p && q  is  "if p then q else false"
||    or条件     p || q  is  "if p then true else q"
!     否定       !p      is  "not p"

アドレス演算子

アドレス演算子&は、オペランドのアドレスを生成します。このときオペランドはアドレス指定可能でなければならず、また変数、ポインタの間接参照、配列またはスライスのインデックス操作、アドレス指定可能な構造体のフィールドセレクタのいずれかでなければなりません。関数の戻り値はアドレス指定可能ではありません。オペランドとしてポインタ型を取るポインタの間接参照演算子*は、オペランドによって指し示されている値を取り出します。

&x
&a[f(2)]
*p
*pf(x)

通信演算子

チャネルという用語は「チャネル型の値」を意味します。

送信操作には、二項演算子“<-”を使用します。この演算子はチャネルと値(式)に作用します。

ch <- 3

送信操作によって、チャネル上に値を送信します。チャネルと式は通信を開始する前に評価されます。通信は、送信が実行可能となるまでブロックされ、可能になると値はチャネルに送られます。バッファリングされていないチャネルへの送信は、受信側の準備ができているときに実行可能です。バッファリングされているチャネルへの送信は、バッファに空きがあるときに実行できます。

送信操作が、式のコンテキスト中に現れるならば、その式の値は論理値であり、操作はブロックされません。通信が行われたときは、その論理値の値はtrueとなります。行われなかったときはfalseになります。(成否とは関係なく、チャネルと送信される式は評価が行われます。)
次の2つの例は等しい内容です。

ok := ch <- 3;
if ok { print("sent") } else { print("not sent") }

if ch <- 3 { print("sent") } else { print("not sent") }

言い換えるなら、プログラムが送信操作の値をチェックするときは、送信はブロックされず、その式の値は操作の結果となります。プログラムで値をチェックしなければ、それが行われるまで操作はブロックし続けます。

受信操作には、単項演算子“<-”を使用します。この式の値は受信した値であり、その型はこのチャネルの要素型です。

<-ch

値が利用可能になるまで式はブロックされ、そのあとは変数に代入するなど、他の式と同じように利用できます。受信した値を受け取らなければ、その値は破棄されます。

v1 := <-ch
v2 = <-ch
f(<-ch)
<-strobe  // クロックパルス待ち

受信式が代入、または初期化で使われるときは次の形式になります。

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

この受信操作のときはブロックされません。受信操作が実行できるときは、論理値型の変数oktrue が、xには受信した値が格納されます。そうでなければokにはfalse がセットされ、xにはその型のゼロ値がセットされます。(§ゼロ値)

メソッド式

メソッドMが、型Tのメソッド群に含まれているとき、Mの引数リストの先頭にこのメソッドのレシーバを加えることで、 T.Mを普通の関数として呼び出すことができます。

MethodExpr    = ReceiverType "." MethodName .
ReceiverType  = TypeName | "(" "*" TypeName ")" .

2つのメソッド、Mv(レシーバは型 T)とMp(レシーバは型*T)を持つ構造体型Tについて考えてみます。

type T struct {
	a int;
}
func (tv  T) Mv(a int)   int   { return 0 }  // 非ポインタのレシーバ
func (tp *T) Mp(f float) float { return 1 }  // ポインタのレシーバ
var t T;

次の式をみてください。

T.Mv

これは、Mvメソッドとは等しいですが、必ず引数の先頭にレシーバを持った関数です。この関数のシグネチャは、次のようになります。

func (tv T, a int) int

この関数は明示的にレシーバを指定することで、通常通り呼び出すことができます。下の3つの呼び出しは等価です。

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

次の同じような式をみてください。

(*T).Mp

これは、Mpメソッドを表す関数値であり、次のシグネチャを持っています。

func (tp *T, f float) float

非ポインタのレシーバを持つメソッドから、ポインタのレシーバを持った関数を得ることができます。これは次のようになります。

(*T).Mv

これは、Mv メソッドを表す関数値であり、次のシグネチャを持っています。

func (tv *T, a int) int

このような関数では、本来のメソッドのレシーバへ受け渡す値を得るためにレシーバを間接参照します。メソッドは、関数呼び出しで渡されたこのアドレスの値を上書きすることはありません。

最後のケースとして、ポインタのレシーバを持つメソッドを、非ポインタのレシーバ関数として使用することはできません。なぜならポインタのレシーバを持つメソッドは、この型のメソッド群には含まれていないからです。

メソッドから取得した関数の値は、関数の呼び出し構文によって呼び出されます。このときレシーバは、呼び出しの引数の先頭に与えられます。つまり、f := T.Mvのとき、ft.f(7)ではなく、 f(t, 7)として実行します。レシーバにバインドする関数を作成するには、クロージャを使ってください。

インタフェース型のメソッドから関数の値を引き出すこともできます。結果得られる関数は、そのインタフェース型のレシーバを必ず受け取ります。

変換

変換とは、Tが型であり、xが型Tへ変換可能な式であるとき、T(x)で表される式です。

Conversion = LiteralType "(" Expression ")" .

一般的に変換は、xの値が型T代入の適合性を持っているとき、あるいは値と型Tが代入の整合性をとりうるとき、あるいは値の型、T、またはこれらのコンポーネント型が名前を持たないときに成功します。通常、このような変換は、xの値でなく型を変更するため、実行時にはコストがかかりません。

変換規則は、Tが数値型または文字列型である変換に適用されます。これらの変換によって値の内容が変わったり、実行コストが増えたりする可能性があります。

整数型間の変換

値が符号を持った数値であるならば、無限精度まで暗黙的に符号拡張されます。さもなければ、その値はゼロ拡張されます。そのあとで変換結果となる型に合うように精度が切り捨てられます。たとえば、x := uint16(0x10F0)ならば、uint32(int8(x)) == 0xFFFFFFF0になります。変換の結果は常に有効な値となり、オーバフローは起こしません。

浮動小数点型を含む変換

  1. 浮動小数点数を整数に変換するとき、小数部は捨てられます(ゼロに近づくよう切り捨て/切り上げ)。
  2. 数値を浮動小数点型に変換するとき、結果となる値はその浮動小数点型で規定されている精度に丸められます。たとえば、float32型の変数xの値は、格納されるときIEEE-754 32ビットの数値以上の精度が使われます。しかし、float32(x)が表すのは32ビットに丸められたxの値です。同様に、 x + 0.1は32ビット以上の精度が使われますが、float32(x + 0.1)はそうなりません。

浮動小数点値を含んでいるすべての変換において、結果となる型が値を表現することができなければ、変換自体は成功しますが、結果となる値は実装依存となります。

文字列への変換

  1. 整数値を変換することで、その整数が表すUTF-8文字を持つ文字列が得られます。
    string(0x65e5)  // "\u65e5" == "日" == "\xe6\x97\xa5"
  2. 整数のスライスを変換することで、各整数を文字列へ変換したあと結合した文字列が得られます。スライスの値がnilであれば、結果は空文字列になります。
    string([]int{0x767d, 0x9d6c, 0x7fd4})  // "\u767d\u9d6c\u7fd4" == "白鵬翔"
  3. バイトのスライスを変換することで、スライス内の連続したバイトデータをそのまま持った文字列が得られます。スライスの値がnilであれば、結果は空文字列になります。
    string([]byte{'h', 'e', 'l', 'l', 'o'})  // "hello"

ポインタと整数間で変換を行う仕組みは言語上はありません。ただしunsafeパッケージでは、ある程度の制限はありますがこの機能を実装しています。

定数式

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

論理値型、数値型、文字列型定数がオペランドとして合法的に使えるときは常に、型を持たない論理値型、数値型、文字列型定数をそれぞれオペランドとして使用できます。シフト演算を除き、二項演算のオペランドが型を持たない整数定数と、同じく型を持たない浮動小数点定数であるとき、整数定数は型を持たない浮動小数点定数に変換されます(/%がこれに相当します)。

結果がbool型となる比較演算子を除き、型を持たない定数に演算子を適用した結果は、同種(すなわち、論理値型、整数型、浮動小数点型、文字列型定数いずれか)の型を持たない定数となります。

定数式は、常に正確に評価されます。そのため評価中の値と定数は、言語でサポートされている事前定義済みの型よりかなり大きな精度を必要とするかもしれません。よって以下は、正しい宣言です。

const Huge = 1 << 100;
const Four int8 = Huge >> 98;

型を持っている定数の値は常に、その定数の型の値を正確に表せなければなりません。よって以下の定数式は正しくありません。

uint(-1)       // -1はuintでオーバフローを起こす
int(3.14)      // 3.14はintegerで切り捨てられる
int64(Huge)    // 1<<100はint64でオーバフローを起こす
Four * 300     // 300はint8でオーバフローを起こす
Four * 100     // 400はint8でオーバフローを起こす

単項のビット補数演算子^を使用したマスクは、非定数の規則と適合します。このマスクは符号なし定数のときは全て1に、符号あり、または型を持たない定数のときは-1になります。

^1          // 型を持たない整数定数。-2に等しい
uint8(^1)   // エラー。 uint8(-2)と同じで、範囲外
^uint8(1)   // uint8型の定数。0xFF ^ uint8(1) = uint8(0xFE)となる
int8(^1)    // int8(-2)と同じ
^int8(1)    // -1 ^ int8(1) = -2となる

評価の順番

代入または式の要素を評価するときには、全ての関数呼び出し、メソッド呼び出し、通信操作は、字句ごとに左から右へと順に評価されます。

代入例です。

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

これら関数の呼び出しと通信は、f()h()i()j()<-cg()k()の順で起こります。しかし、これら関数呼び出しと通信の順番は、xのインデックス指定と評価、およびyの評価と比べると未定義です。

単一式中の浮動小数点演算は、演算子がもつ結合法則に従って評価されます。明示的な括弧は、規定の結合法則を上書きすることで評価に影響を及ぼします。式x + (y + z)では、xを加える前にy + zの加算が行われます。

ステートメント

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

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

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

StatementList = Statement { Separator Statement } .
Separator     = [ ";" ] .

ステートメントリスト(StatementList)の要素はセミコロンで区切られます。ただし直前のステートメントが次のときだけはセミコロンは省略可能です。

  • 宣言リストが、右丸括弧“)”で終わっているとき。もしくは、
  • 右波括弧“}”で終わっていて、それが式の一部ではないとき。

空ステートメント

空ステートメントは、何も行いません。

EmptyStmt = .

空ステートメントを加えることで、ステートメントリストをいつでも実質的にセミコロンで終了させることができます。

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

ラベル付きステートメントは、gotobreakcontinueステートメントの宛先となります。

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

式ステートメント

関数呼び出し、メソッド呼び出し、チャネル操作は、ステートメントの文脈内に記述することができます。

ExpressionStmt = Expression .
f(x+y)
<-ch

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

“++”と“–”ステートメントは、型を持たない定数1を使って、オペランドをインクリメントまたはデクリメントします。代入を伴うときは、オペランドは変数、ポインタ間接参照、フィールドセレクタ、インデックス式のいずれかでなければなりません。

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

以下の代入ステートメントは、同じ意味合いです。

Inc/Dec             代入
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

代入操作 x op= yにおいて、opが二項算術演算子のとき、x = x op yと同等ですが、このときxが評価されるのは一度だけです。また、このop=はひとつのトークンを構成します。代入操作において、左辺、右辺双方の式リスト(ExpressionList)は、単一値となるひとつの式でなければなりません。

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

組み合わせ代入は、複数値となる操作の各要素を、変数リストに個別に代入します。これには2つの形式があります。1番目の形式では、右辺のオペランドは、関数の評価、チャネルマップ操作、型アサーションのような、ひとつの複数値となる式です。左辺のオペランドの数は、右辺の値の数と一致しなければなりません。例えば、fが2つの値を返す関数のときは、

x, y = f()

これは、1番目の値をxに、2番目の値をyに代入します。ブランク識別子を使うと、複数値となる式から返された値を無視することができます。

x, _ = f()  // f()から返される2番目の値を無視

2番目の形式では、左辺のオペランドの数は、右辺の式の数と一致し、かつ各右辺の式はすべて単一値とならなければなりません。右辺のn番目の式は、左辺のn番目のオペランドに代入されます。このとき右辺の式が評価されるタイミングは、左辺のオペランドの何れかに代入が行われるより前に行われます。でなければ評価の順序規則から外れてしまうためです。

a, b = b, a  // aとbを入れ替え

代入において、それぞれの値は代入先オペランドの型と代入の適合性を持たなければなりません。型を持たない定数をインタフェース型の変数へ代入するとき、その定数はboolintfloatstring型いずれかの値へ変換されます。どの型になるかは、値が論理値、整数、浮動小数点、文字列型定数のどれであるかに依存します。

ifステートメント

ifステートメントは、2つに分岐したロジックを論理値型の式の値に従って実行します。式の評価結果がtrueとなるときは、if側のロジックが実行されます。falseとなるときに、elseが記述されていれば、それが伴うロジックが実行されます。条件が記述されなかったときはtrueと記述したことと同等です。

IfStmt    = "if" [ SimpleStmt ";" ] [ Expression ] Block [ "else" Statement ] .
if x > 0 {
	return true;
}

シンプルステートメント(SimpleStmt)が、式(Expression)の直前にあるときは、式が評価されるより前にシンプルステートメントが実行されます。

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

switchステートメント

switchステートメントは、複数に渡る分岐の実行を行います。どの分岐を実行すべきか判断するため、式または型指定と、switch内のcaseとが比較されます。

SwitchStmt = ExprSwitchStmt | TypeSwitchStmt .

switchステートメントには、式switchと型switchの2つの形式があります。式switchcaseには、switch式の値と比較するための式が含まれます。型switchswitchには、switch式にて特殊な形式で示された型と比較するための型が含まれます。

式switch

switchにおいては、switchの式が評価されたあと、case式(定数である必要はない)が、左から右へ、上から下へと評価されていきます。switch式と等しい最初のcaseが見つかると、それが伴うステートメントが実行され、それ以外のcaseはスキップされます。一致するcaseがないときにdefaultケースがあれば、そのステートメントが実行されます。defaultケースは最大でも1つまでしか記述できませんが、switchステートメント内のどこにでも記述することができます。式が記述されなかったときはtrueと記述したことと同等です。

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

caseまたはdefault節内の最終ステートメントだけには、制御が次のcaseまたはdefault節の先頭ステートメントへと流れるべきであることを示す、fallthroughステートメント(§fallthroughステートメント)を記述することができます。これが記述されないときは、制御の流れはswitchステートメントの終わりへ移ります。

シンプルステートメント(SimpleStmt)が、式(Expression)の直前にあるときは、式が評価されるより前にシンプルステートメントが実行されます。

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

switch x := f(); {
case x < 0: return -x
default: return x
}

switch {  // 式が記述されていないので"true"として扱われる
case x < y: f1();
case x < z: f2();
case x == 4: f3();
}

型switch

switchは、値の代わりに型を比較します。その他の点では、式switchとほぼ同じです。型switchは、予約語typeを型名の代わりとして使った型アサーションの形式を持つ特殊なswitch式であることによって識別されます。caseはリテラルの型と、型アサーションの式が示す動的な型とを比較します。

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

型スイッチガード(TypeSwitchGuard)には、省略形式による変数の宣言を含むことができます。この形式が使われるとき、各case/default節内でその変数が宣言されます。リスト(TypeList)内に型をひとつだけ指定したcase節では、この変数はcaseで指定した型を持ちます。型を複数指定したときは、変数は型スイッチガードの式が表す型となります。

caseに指定する型をnil事前宣言済み識別子)とすることもできます。このcaseは、型スイッチガード内の式がnilインタフェース値であるときに選択されます。

下は、interface{}型の値を返す関数fを使った、型switchです。

switch i := f().(type) {
case nil:
	printString("f() returns nil");
case int:
	printInt(i);  // i is an int
case float:
	printFloat(i);  // i is a float
case func(int) float:
	printFunction(i);  // i is a function
case bool, string:
	printString("type is bool or string");  // i is an interface{}
default:
	printString("don't know the type");
}

これは、下のように書き直すこともできます。

v := f();
if v == nil {
	printString("f() returns nil");
} else if i, is_int := v.(int); is_int {
	printInt(i);  // i is an int
} else if i, is_float := v.(float); is_float {
	printFloat(i);  // i is a float
} else if i, is_func := v.(func(int) float); is_func {
	printFunction(i);  // i is a function
} else {
	i1, is_bool := v.(bool);
	i2, is_string := v.(string);
	if is_bool || is_string {
		i := v;
		printString("type is bool or string");  // i is an interface{}
	} else {
		i := v;
		printString("don't know the type");  // i is an interface{}
	}
}

シンプルステートメント(SimpleStmt)が、型スイッチガードの直前にあるときは、型スイッチガードが評価されるより前にシンプルステートメントが実行されます。

fallthroughステートメントは、型switchにおいては許可されていません。

forステートメント

forステートメントは、ブロックの繰り返し実行を行います。繰り返しは、条件(Condition)、for節(ForClause)、range節(RangeClause)のいずれかによって制御されます。

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

最も単純な形式のforステートメントでは、条件の評価結果の論理値がtrueとなっている間、ブロックの実行を繰り返します。条件は繰り返しが行われる直前に毎回評価されます。条件が記述されなかったときはtrueと記述したことと同等です。

for a < b {
	a *= 2
}

for節(ForClause)によるforステートメントも条件(Condition)によってコントロールされますが、それに加えて、代入、インクリメント、デクリメントなどを行う初期化ステートメント(InitStmt)、またはポストステートメント(PostStmt)を記述できます。初期化ステートメントは省略形式による変数の宣言ですが、ポストステートメントはそうである必要はありません。

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

初期化ステートメントが空でなければ、繰り返しの初回に条件が評価される直前に一度だけ実行されます。ポストステートメントはブロックを実行した直後、(ブロックが実行されたときだけ)実行されます。for節の各要素は空にすることができますが、条件だけを記述した場合を除き、セミコロンの記述が必要です。条件が指定されなかったときはtrueと記述したことと同等です。

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

range節によるforステートメントは、配列、スライス、文字列、マップ、チャネルから受信した値、これらの全エントリを使って繰り返しを行います。エントリ毎にまず、カレントのインデックスまたはキーをイテレーション変数に代入するか、もしくはカレントの「インデックスと要素のペア」または「キーと値のペア」をそれと対になるイテレーション変数に代入します。そのあとでブロックが実行されます。

RangeClause = ExpressionList ( "=" | ":=" ) "range" Expression .

range節の右側の式の型は、配列、スライス、文字列、マップ、配列へのポインタ、チャネルのどれかでなければなりません。チャネルのときを除いて、左辺の識別子リスト(ExpressionList)は、1~2個の式(代入と同じく、これらは変数、ポインタ間接参照、フィールドセレクタ、インデックス式のどれか)でなければなりません。各繰り返しにおいて、1番目のイテレーション変数にセットされるのは文字列または配列またはスライスのインデックス、マップのキーのいずれかであり、2番目のイテレーション変数があれば、それにセットされるのは1番目の変数と対応する文字列、配列要素、マップの値です。配列またはスライスのインデックスの型(これは常にint)、要素の型、マップのキーおよび値の型は、対応するイテレーション変数に対して代入の適合性を持っていなければなりません。

文字列を扱うとき、range節は文字列内のユニコードポイントを繰り返します。連続する繰り返しの際、インデックス用変数にセットされる値は、文字列中のUTF-8エンコードされたコードポイントの連続したバイト列の先頭のインデックスであり、2番目のint型の変数にセットされる値は、それと対応するコードポイントの値です。繰り返しの最中に無効なUTF-8シーケンスが現れたときは、2番目の変数は0xFFFD(Unicode replacement character)となり、次の繰り返しのときに文字列内を1バイト進めます。

チャネルを扱うときは、識別子リスト(ExpressionList)には、識別子がひとつだけでなければなりません。ループはチャネルがクローズされるまでチャネル上に送られた値を受信し続けますが、チャネルがクローズされるまでは、送られたゼロ値は処理されません。

イテレーション変数が、range節(“:=”)によって宣言されるとき、この変数のスコープは、forステートメントの終了までです(§宣言とスコープ)。このときこれらイテレーション変数の型は、それぞれint型と配列要素型のセット、もしくはマップのキーと値のセットのどちらかとなります。イテレーション変数がforステートメント外で宣言されているとき、その変数の実行後の値は、最後に繰り返した状態です。

var a [10]string;
m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6};

for i, s := range a {
	// iの型はint
	// sの型はstring
	// s == a[i]
	g(i, s)
}

var key string;
var val interface {};  // mの値の型は、valに対して代入の適合性がある
for key, val = range m {
	h(key, val)
}
// key == マップの繰り返し内で最後に現れたエントリのキー
// val == map[key]

繰り返し中に、まだ処理されていないマップエントリが削除されたときは、そのエントリが処理されることはありません。また繰り返し中に、マップエントリが追加されたときの動作は実装に依存しますが、ひとつのエントリが複数回処理されることはありません。

goステートメント

goステートメントは、独立した並列スレッドまたはゴルーチン(goroutine)として、関数またはメソッドの実行を同一アドレス空間で開始します。

GoStmt = "go" Expression .

この式(Expression)は、関数またはメソッドの呼び出しでなければなりませんが、通常の呼び出しとは異なり、プログラムは実行された関数の完了を待ちません。

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

selectステートメント

selectステートメントは通信可能な集合の中から、実行可能なものを選択します。switchステートメントに似ていますが、すべてのcaseで通信操作を行っている点が異なります。

SelectStmt = "select" "{" { CommClause } "}" .
CommClause = CommCase ":" StatementList .
CommCase = "case" ( SendExpr | RecvExpr) | "default" .
SendExpr =  Expression "<-" Expression .
RecvExpr =  [ Expression ( "=" | ":=" ) ] "<-" Expression .

selectステートメント内のすべての送信・受信式において、チャネル式(送信式の右側の式も含めて)は、上から下へと順番に評価されます。selectステートメントの結果としてcaseがひとつ以上実行可能となると、その内のひとつが選択され、それが伴う通信処理とステートメントが評価されます。どれも実行可能とならないとき、defaultケースがあれば実行されますが、defaultケースがないときは、通信のどれか1つが実行可能となるまで、ステートメントはブロックします。チャネルおよび送信式が複数回評価されることはありません。チャネルのポインタがnilのときは、selectステートメント内にそのcaseが存在しないことと同等ですが、送信のときは式だけは評価されます。

まず、selectステートメント内のすべてのチャネルおよび送信式が評価されてから、その評価の二次的作用が通信に対して起こります。

実行可能なcaseが複数あるときは、平等かつ公平に選択が行われ、実行する通信がひとつだけ決定されます。

受信のcaseには、省略形式による変数の宣言を使って新しい変数を宣言することもできます。

var c, c1, c2 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");
default:
	print("no communication\n");
}

for {  // ランダムなビットシーケンスをcに送信
	select {
	case c <- 0:  // note: no statement, no fallthrough, no folding of cases
	case c <- 1:
	}
}

returnステートメント

returnステートメントは、それが記述されている関数の実行を終了し、必要であれば戻り値として単一または複数の値を呼び出し元に返します。

ReturnStmt = "return" [ ExpressionList ] .

戻り値を持たない関数のreturnステートメントは、戻り値を返してはいけません。

func no_result() {
	return
}

戻り値を持つ関数から戻り値を返すには、3通りの方法があります。

  1. 戻り値として単一値または複数値がreturnステートメントで明示的にリストされます。個々の式は単一値であり、かつ対応する関数の戻り値の型と、代入の適合性を持っていなければなりません。
    func simple_f() int {
    	return 2
    }
    
    func complex_f1() (re float, im float) {
    	return -7.0, -4.0
    }
  2. returnステートメントの式リストは、複数値を返す関数の(一回の)呼び出しです。これは関数から返されたそれぞれの値が、適切な型を持ったテンポラリの変数に代入され、その変数がreturnステートメントにリストされるかのように振る舞います。そのあとは、前のケースで説明した規則が当てはまります。
    func complex_f2() (re float, im float) {
    	return complex_f1()
    }
  3. 関数の戻り値のパラメータに名前をつけているときは、returnの式を空にすることができます(§関数型)。戻り値パラメータは、通常のローカル変数と同様に、その型のゼロ値(§ゼロ値)で初期化され、関数内で必要に応じて値の代入が行われます。returnステートメントは、これら変数に格納されている値を返します。
    func complex_f3() (re float, im float) {
    	re = 7.0;
    	im = 4.0;
    	return;
    }

breakステートメント

breakステートメントは、最も内側にあるforswitchselectステートメントの実行を終了します。

BreakStmt = "break" [ Label ].

ラベルが指定されているときは、そのラベルはforswitchselectステートメントのどれかを伴っていなければならず、breakステートメントによって、これらステートメントが終了します(§forステートメント、§switchステートメント、§selectステートメント)。

L: for i < n {
	switch i {
		case 5: break L
	}
}

continueステートメント

continueステートメントは、最も内側のforループのポストステートメントから次の繰り返しを開始します (§forステートメント)。

ContinueStmt = "continue" [ Label ].

オプションとして指定可能なラベルは、breakステートメントのラベルと同じです。

gotoステートメント

gotoステートメントは、指定したラベルを持つステートメントへ制御を移します。

GotoStmt = "goto" Label .
goto Error

gotoステートメントの実行が原因で、それまでスコープ外だった変数が、スコープ内に入るようなケースは許されません。たとえば、この例などが相当します。

goto L;  // BAD
v := 3;
L:

この例は、ラベルLへジャンプすることで、変数vの作成をスキップしてしまうため誤りです。

fallthroughステートメント

fallthroughステートメントは、式switch式switch)の次のcase節の先頭のステートメントへ制御を移します。このfallthroughステートメントが記述できるのは、式switch内のcaseまたはdefault節の、空ではない最終ステートメントだけです。

FallthroughStmt = "fallthrough" .

deferステートメント

deferステートメントは、deferステートメントを記述している関数自体が復帰するまでの間、指定した関数の実行を先延ばしします。

DeferStmt = "defer" Expression .

deferに指定する式は、関数またはメソッドの呼び出しでなければなりません。deferステートメントが実行される度に、関数呼び出しのパラメータは評価され、評価結果が保存されますが、関数の実行は行われません。保留された関数呼び出しは、deferステートメントを記述している関数から復帰する直前(戻り値があれば、その評価後)に、LIFOの順序で実行されます。

lock(l);
defer unlock(l);  // この関数から復帰する直前にunlockが行われる

// この関数から復帰する直前に、3 2 1 0と出力される
for i := 0; i <= 3; i++ {
	defer fmt.Print(i);
}

組み込み関数

組み込み関数として、いくつかの関数が事前宣言済みとなっています。組み込み関数の呼び出しは他の関数と同様ですが、いくつかの関数では、第一引数に「式」ではなく「型」を受け取ります。

BuiltinCall = identifier "(" [ BuiltinArgs ] ")" .
BuiltinArgs = Type [ "," ExpressionList ] | ExpressionList .

close、closed

cをチャネルと仮定したとき、事前宣言済み関数close(c)は、これ以上の値を送信操作により受け付けないようチャネルをマーキングします。closeより前に送信された値を受信したあとは、受信操作はそのチャネル型のゼロ値を返すようになります。このようにゼロ値が受信されたあとは、closed(c)trueを返すようになります。

長さ、キャパシティ

組み込み関数lencapは、様々な型の引数を取り、int型の結果を返します。実装上、これらが返す結果が常にintと適合することが保証されています。

呼び出し   引数の型              戻り値

len(s)    文字列型              文字列のバイト長
          [n]T, *[n]T          配列の長さ(== n)
          []T                  スライスの長さ
          map[K]T              マップの長さ(定義されているキーの数)
          chan T               チャネルのバッファ内でキューイングされている要素数

cap(s)    [n]T, *[n]T          配列の長さ(== n)
          []T                  スライスのキャパシティ
          chan T               チャネルのバッファのキャパシティ

スライスのキャパシティは、根底にある配列に割り当てられている要素数です。これは常に、下の関係が保たれます。

0 <= len(s) <= cap(s)

メモリの割当

組み込み関数newは型Tを取って、型*Tの値を返します。このときメモリの内容は初期値のセクション(§ゼロ値)で記述されているように初期化されます。

new(T)

例です。

type S struct { a int; b float }
new(S)

この例では、型Sの変数に対してメモリを動的に割り当て、初期化(a=0, b=0.0)したのち、そのメモリのアドレスを持つ、型*Sの値を返します。

スライス、マップ、チャネルの作成

スライス、マップ、チャネルは、newによる間接的なメモリ割り当てを必要としない、参照型です。
組み込み関数makeは、型Tを取ります。このTはスライス、マップ、チャネル型でなければなりません。また、オプションとして式のリスト(作成する種類によって異なる)を取ります。makeは型T(*Tではない)を返します。このときメモリの内容は初期値のセクション(§ゼロ値)で記述されているように初期化されます。

make(T [, 式のリスト(オプション)])

例です。

make(map[string] int)

この例では、新しいマップの値を作成し、空のマップとして初期化します。

パラメータの値によって、スライス、マップ、バッファリングされたチャネルの割り当てサイズが変更できます。

s := make([]int, 10, 100);        // len(s) == 10, cap(s) == 100のスライス
s := make([]int, 10);             // len(s) == cap(s) == 10のスライス
c := make(chan int, 10);          // バッファサイズ10のチャネル
m := make(map[string] int, 100);  // 100要素を初期容量として持つマップ

ブートストラッピング

現在の実装では、ブートストラッピング(Go言語コンパイラ自身のコンパイル)に役立つ、いくつかの組み込み関数を提供しています。これらの関数は文書化されていますが、このまま言語に残されるかどうかは保証されません。また、これらの関数は結果を返しません。

関数        振舞い

print      全引数を出力する(各フォーマットは実装依存)
println    printと同じだが、各引数間にスペース、および最後に改行を出力する
panic      printと同じだが、出力後に実行を中断する
panicln    printlnと同じだが、出力後に実行を中断する

パッケージ

Go言語のプログラムは、複数のパッケージをひとつにリンクすることによって作られます。さらに各パッケージは、そのパッケージに所属する定数、型、変数、関数を宣言している、ひとつ以上のソースファイルから構成されます。これらパッケージ内の要素は、同一パッケージ内であれば、別ファイルからアクセスすることができます。また要素がエクスポートされていれば、他パッケージからアクセスすることができます。

ソースファイルの構成

各ソースファイルは、そのファイルがどのパッケージに属しているかを定義するパッケージ文で始まります。以降は必須ではありませんが、ソースファイル内で使用したいパッケージを宣言する一連のインポート宣言。さらに、関数、型、変数、定数の一連の定義が続きます。

SourceFile       = PackageClause { ImportDecl [ ";" ] } { TopLevelDecl [ ";" ] } .

パッケージ文

各ソースファイルはパッケージ文で始まり、そのファイルが所属するパッケージを定めます。

PackageClause  = "package" PackageName .
PackageName    = identifier .

このパッケージ名(PackageName)はブランク識別子であってはなりません。

package math

パッケージの実装は、同じパッケージ名を共有するファイル群によって構成されます。実装によっては、同一パッケージ内のすべてのソースファイルが、同一ディレクトリ内に置かれている必要があります。

インポート宣言

インポート宣言によって、インポートされたパッケージ内でエクスポートされている識別子を使うことで、インポート宣言を記述しているソースファイルから、それらにアクセス可能になります。インポートでは、アクセスするために使用する識別子(PackageName)、およびインポートされるパッケージを指定するImportPathを指定します。

ImportDecl       = "import" ( ImportSpec | "(" [ ImportSpecList ] ")" ) .
ImportSpecList   = ImportSpec { ";" ImportSpec } [ ";" ] .
ImportSpec       = [ "." | PackageName ] ImportPath .
ImportPath       = StringLit .

PackageNameは、限定付き識別子を使い、そのパッケージでエクスポートされている識別子にアクセスするために使用されます。PackageNameは、ファイルブロック内で宣言されます。PackageNameを省略したときは、インポートされた側パッケージのパッケージ文で指定されている識別子がデフォルトとして使用されます。名前の代わりにピリオド(.)が指定されたときは、そのパッケージでエクスポートされている全識別子が、カレントのファイルのファイルブロックで宣言され、プレフィックスなしでアクセスできるようになります。

ImportPathの解釈は実装に依存しますが、一般的には、コンパイル済みパッケージのパス名の一部であり、パッケージのリポジトリへの相対パスです。

仮に、Sin関数をエクスポートしているmathパッケージがあり、"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"

// 2,3,4,...というシーケンスをチャネル'ch'に送信
func generate(ch chan<- int) {
	for i := 2; ; i++ {
		ch <- i;	// 'i'をチャネル'ch'に送信
	}
}

// チャネル'in'の値をチャネル'out'にコピー
// ただし'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();
}

プログラムの初期化と実行

ゼロ値

値を格納するため宣言や、make()new()の呼び出しによりメモリが割り当てられたとき、明示的に初期化をしなければ、そのメモリはデフォルトの初期化が行われます。このような値の要素には、それぞれその型のゼロ値がセットされます。論理値型のゼロ値はfalse、整数型は0、浮動小数点型は0.0、文字列型は""です。ポインタ、関数、インタフェース、スライス、チャネル、マップ型のゼロ値はnilです。この初期化は再帰的に行われるので、たとえば構造体が配列のとき、初期値が指定されなければ、各要素のフィールドはゼロ値となります。

次の2つの宣言は等価です。

var i int;
var i int = 0;

続いて、

type T struct { i int; f float; next *T };
t := new(T);

これは、下の値を持ちます。

t.i == 0
t.f == 0.0
t.next == nil

次も同じことが当てはまります。

var t T

プログラムの実行

インポートを伴わないパッケージの初期化は、パッケージレベルのすべての変数への初期値代入、およびソース内で定義されている次の名前とシグネチャを持ったパッケージレベルの関数の呼び出しにより行われます。

func init()

パッケージに複数のinit()関数が含まれるとき(ひとつのソースファイルに複数記述も可)は、順不同で実行されます。

パッケージ内で、パッケージレベルの変数の初期化、および定数値の決定は、それぞれのデータの依存する順で行われます。たとえばAのイニシャライザがBの値に依存するならば、Aの値はBの後に決まります。この依存関係がループしてしまうときはエラーとなります。
依存関係の分析は、辞書的かつ再帰的に行われます。Aの値がBに影響を受けている、またはAのイニシャライザがBの影響を受けている、またはAが影響を受けている関数がさらにBの影響を受けているときに、ABに依存しているとみなされます。
ある2つのアイテムがお互いに依存していなければ、それらはソース内に出現した順で初期化されます。依存関係の分析はパッケージごとに行われるため、Aのイニシャライザが、Bを参照している別のパッケージで定義されている関数を呼び出したときの結果は規定されていません。

初期化コードが、“go”ステートメントを含んでいたとしても、全プログラムの初期化が終了するまで、そのステートメントで指定した関数の実行を開始しません。したがって、すべての初期化コードは単一のゴルーチン内で動作します。

init()関数はプログラムのどこからも参照することができません。すなわち、明示的に呼び出すことも、変数にこの関数のポインタを代入することもできません。

パッケージがインポートを伴うときは、このパッケージ自身の初期化より前に、インポートされたパッケージが初期化されます。ひとつのパッケージを複数回インポートしても、そのパッケージが初期化されるのは一回だけです。

パッケージのインポートでは構造上、初期化の依存関係が循環しないことが保証されています。

完成されたプログラムは、次の関数を持つmainパッケージを持たなくてはなりません。

func main() { ... }

これは、複数のパッケージを結合して作られたとしても同様です。このmain.main()関数は、引数および戻り値を持ちません。

プログラムの実行はmainパッケージを初期化したあと、main.main()関数を実行することで開始されます。

main.main()から抜けたときに、プログラムは終了します。このとき、他(main以外)のゴルーチンの終了待ちは行いません。

実装上の制約:コンパイラはmainパッケージが、他のどのパッケージからもインポートされることはないと仮定しています。

システム考察

unsafeパッケージ

組み込みパッケージunsafeは、コンパイラに実装されており、低レベルのプログラミング向けの機能を提供します。これには型システムから逸脱した機能が含まれています。そのためunsafeを使っているパッケージは、型が安全であることを手作業でよく確認しておかなくてはなりません。このパッケージでは、以下のインタフェースを提供しています。

package unsafe

type ArbitraryType int  // shorthand for an arbitrary Go type; it is not a real type
type Pointer *ArbitraryType

func Alignof(variable ArbitraryType) int
func Offsetof(selector ArbitraryType) int
func Sizeof(variable ArbitraryType) int

func Reflect(val interface {}) (typ runtime.Type, addr uintptr)
func Typeof(val interface {}) reflect.Type
func Unreflect(typ runtime.Type, addr uintptr) interface{}

すべてのポインタ、およびuintptr型の値は、Pointerに変換することができます。またその逆も可能です。

Sizeof関数は、変数を表す式を受け取り、その変数のサイズをバイト数で返します。

Offsetof関数は、構造体のフィールド表すセレクタ(§セレクタ)を受け取り、構造体のアドレスからフィールドへの相対オフセットをバイト数で返します。下は構造体sのフィールドfを使った例です。

uintptr(unsafe.Pointer(&s)) + uintptr(unsafe.Offsetof(s.f)) == uintptr(unsafe.Pointer(&s.f))

コンピュータのアーキテクチャによっては、メモリのアドレスにアライメントが必要となることがあるため、変数のアドレスは、変数の型が持つアライメントも考慮します。Alignof関数は、変数を表す式を受け取り、変数(の型)のアライメントをバイト数で返します。下は変数xを使った例です。

uintptr(unsafe.Pointer(&x)) % uintptr(unsafe.Alignof(x)) == 0

AlignofOffsetofSizeofの呼び出しは、コンパイル時にint型の定数となります。

unsafe.Typeofunsafe.Reflectunsafe.Unreflect関数は、実行時にインタフェースに格納されている動的な型および値へのアクセスを許します。Typeofは、valの動的な型を runtime.Typeとして返します。Reflectは、valの動的な値のコピーを割り当て、型とそのコピーのアドレスを返します。UnreflectReflectの逆で、型とアドレスからインタフェースの値を作成します。これらの関数を利用しているreflectパッケージでは、インタフェースの値を安全、かつより便利に調べる方法を提供しています。

サイズとアライメントの保証

数値型(§数値型)では、以下に示すサイズが保証されています。

型                        サイズ(バイト数)

byte, uint8, int8         1
uint16, int16             2
uint32, int32, float32    4
uint64, int64, float64    8

以下に示す、アライメントの特性が最低限保証されています。

  1. 変数x(型は問わない):1 <= unsafe.Alignof(x) <= unsafe.Maxalign
  2. 変数xが数値型のとき:unsafe.Alignof(x)は、 unsafe.Sizeof(x)およびunsafe.Maxalign 以下であり、少なくとも1以上である。
  3. 変数xが構造体型のとき:unsafe.Alignof(x)は、xの各フィールドをfとしたときunsafe.Alignof(x.f)の中で一番大きい値と同じであり、少なくとも1以上である。
  4. 変数xが配列型のとき:unsafe.Alignof(x)は、unsafe.Alignof(x[0])と同じであり、少なくとも1以上である。

実装との差異 – TODO

  • 実装は、gotoステートメント、および(宣言以外の)宛先に対する制約を守っていません。
  • メソッド式は未実装です。
  • Gccgoではひとつのソースファイルに、ひとつのinit()関数しか許していません。