このドキュメントは、http://golang.org/ref/specの翻訳です。


Version of September 4, 2012

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

はじめに

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

Go言語は汎用言語ですが、システムプログラミングを念頭に設計されています。この言語は、強い型付け、ガーベージコレクション、並列処理をサポートする機能を持ちます。プログラムはパッケージという単位で構成され、依存関係は効率的に管理されます。今現在は、従来からあるコンパイル/リンクモデルによる実行バイナリ生成方式を採っています。

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

表記法

Go言語の文法は、EBNF(Extended Backus-Naur Form)を使って表記します。

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では、語句および、下に示した演算子(優先度順)による式を組み合わせて定義を行います。

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

イコールの左辺の名称が小文字の場合は、その名称はトークンを識別するために使われます。非終端は大文字と小文字を混在させたキャメルケースで記述します。トークンはダブルクォート""、またはバッククォート``でくくられます。

a … b」形式は、文字aから文字bまでの範囲内の文字を表します。3点リーダ()は正式な仕様を表す以外にも、例えば列挙の省略、コードの一部分を省略するときに使用されます。この文字は、Go言語のトークンではありませんが、ドットを3つ並べた...はトークンです。

ソースの文字コード

ソースの文字コードは、UTF-8でエンコードされたUnicode文字です。テキストは正規化されないため、あるアクセント付きコードポイントは、アクセント記号とアルファベットを結合して造られた同じ文字とは異なる文字として扱われます。後者の文字は2つのコードポイントとみなされます。わかりやすくするため、このドキュメントではソースコード内のUnicodeコードポイントのことを「文字」と表します。

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

実装上の制約:他のツールとの整合性のため、ソーステキスト中のNUL文字(U+0000)は許可されません。

文字

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

newline        = /* Unicodeコードポイント U+000A */ .
unicode_char   = /* 各Unicodeコードポイント(newlineを除く) */ .
unicode_letter = /* 文字のUnicodeコードポイント */ .
unicode_digit  = /* 数字のUnicodeコードポイント */ .

文字カテゴリセットは、The Unicode Standard 6.0のセクション4.5 「General Category」で定義されています。

Go言語では、これらカテゴリのうちLu, Ll, Lt, Lm, LoをUnicode字(レター)、NdをUnicode数字として扱います。

字と数字

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

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

字句要素

コメント

コメントには、2種類の書式があります。

  1. 行コメントは、文字シーケンス//で始まり、行末で終了します。行コメントは改行として扱われます。
  2. 範囲コメントは、文字シーケンス/*で始まり、文字シーケンス*/までがコメントとなります。範囲形式のコメントが1行もしくは複数行にまたがる場合はひとつの改行として扱われ、それ以外はスペースとして扱われます。

コメントを入れ子にすることはできません。

トークン

トークンは、Go言語のボキャブラリを形成し、これには識別子、キーワード、演算子および区切り文字、リテラルの4つのクラスがあります。ホワイトスペースはスペース(U+0020)、水平タブ(U+0009)、キャリッジリターン (U+000D)、改行(U+000A)です。ホワイトスペースは、それがなければ一つのトークンとして結合されてしまうトークンを分割するために使う以外は、無視されます。また、改行またはファイルの終わりは、セミコロンが自動挿入されるトリガとなります。コンパイラなどがソースコードの解析でソースコードをトークンに分割するとき、その次のトークンは長い文字列シーケンスから成る有効なトークンです。

セミコロン

正式な文法としては、ステートメントの終端としてセミコロン";"を使います。ただしGo言語のプログラムでは下の2つの規則を用いてセミコロンの大部分を省略できます:

  1. コンパイラがソースコードをトークンに分割するとき、非空白行の行末のトークンが下のいずれかと一致すれば、セミコロンが自動挿入されます。
    • 識別子
    • 整数、浮動小数点、ルーン、虚数、文字列リテラル
    • 次のキーワードbreakcontinuefallthroughreturn
    • 次の演算子およびデリミタ++--)]}
  2. 一行に複数のステートメントを記述するときでも閉括弧")"または"}"直前のセミコロンは省略できます。

このドキュメント内のサンプルコードでは、慣用的な使用方法を示すためにこれらの規則に従ってセミコロンを省いています。

識別子

識別子は、変数や型といったプログラムの実体に対し名前をつけます。

識別子は、一文字以上の字(レター)と数字から構成されます。先頭一文字は字でなくてはなりません。

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.
72.40
072.40  // == 72.40
2.71828
1.e+0
6.67428e-11
1E6
.25
.12345E+5

虚数リテラル

虚数リテラルは、複素数定数の虚数部分の小数表現です。これは浮動小数定数リテラルまたは小数整数と小文字iで構成されます。

imaginary_lit = (decimals | float_lit) "i" .
0i
011i  // == 11i
0.i
2.71828i
1.e+0i
6.67428e-11i
1E6i
.25i
.12345E+5i

ルーンリテラル

ルーンリテラルは、ルーン定数であり、Unicodeコードポイントに一致する整数値です。ルーンリテラルは一文字以上の文字をシングルクォートでくくって表現します。クォート内にはシングルクォートと改行を除くどんな文字も記述できます。バックスラッシュを使用したマルチ文字シーケンスの各書式でエンコードすることで、シングルクォート文字自体のUnicode値も表せます。

クォートを使うと簡単に一文字を表すことができます。これはGo言語のソースがUTF-8エンコードされたUnicode文字であり、UTF-8エンコードされている複数のバイト列であってもひとつの整数値を表せるからです。

たとえば、リテラル'a'は文字a、Unicode U+0061、値0x61を表す1バイトの値です。一方'ä'は文字a-dieresis、U+00E4、値0xe4を表す2バイト(0xc3 0xa4)の値です。

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

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

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

\a   U+0007 alert or bell
\b   U+0008 backspace
\f   U+000C form feed
\n   U+000A line feed or newline
\r   U+000D carriage return
\t   U+0009 horizontal tab
\v   U+000b vertical tab
\\   U+005c backslash
\'   U+0027 single quote  (ルーンリテラル内でのみ有効なエスケープ)
\"   U+0022 double quote  (文字列リテラル内でのみ有効なエスケープ)

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

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'
'aa'         // 不正: 文字が多すぎる
'\xa'        // 不正: 16進数文字が不足 
'\0'         // 不正: 8進数文字が不足 
'\uDFFF'     // 不正: サロゲートが半分 
'\U00110000' // 不正: Unicodeコードポイント不正

文字列リテラル

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

未加工文字列リテラルは、バッククォート``で囲まれた文字シーケンスです。クォート内にはバッククォート以外の文字すべてを記述できます。この未加工文字列リテラルの示す値は、クォート内の何も解釈(UTF-8エンコードにおいて)されることがない文字から成る文字列で、バックスラッシュも特別な意味を持たず、改行も含められます。未加工文字列リテラルに含まれる復帰コードは取り除かれます。

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

string_lit             = raw_string_lit | interpreted_string_lit .
raw_string_lit         = "`" { unicode_char | newline } "`" .
interpreted_string_lit = `"` { unicode_value | byte_value } `"` .
`abc`  // same as "abc"
`\n
\n`    // same as "\\n\n\\n"
"\n"
""
"Hello, world!\n"
"日本語"
"\u65e5本\U00008a9e"
"\xff\u00FF"
"\uD800"       // 不正: サロゲートが半分
"\U00110000"   // 不正: Unicodeコードポイント不正

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

"日本語"                                 // 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のような組み込み関数からの戻り値、式に対して適用されるcaplen、文字列定数のlen、複素数定数のrealimag、数値定数のcomplexによって表されます。ブーリアンの真理値は事前宣言済み定数trueおよびfalseによって表されます。事前宣言済み識別子iotaは整数リテラルを表します。

一般的に、複素数定数は定数式の一形式であるため、そのセクション内で解説します。

数値定数は、任意精度の値を表し、オーバーフローすることがありません。

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

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

IEEE-754の無限値と非数値を表す定数はありませんが、実行時にmathパッケージのInfNaNIsInfIsNaN関数を使ってこれらの値を扱うことができます。

実装上の制約: 言語仕様として数値定数の精度に制限はありませんが、コンパイラの実装によっては精度が限定されます。すべてのコンパイラは以下を実装しなければなりません。

  • 整数定数は、少なくとも256ビットを表せること。
  • 浮動小数点定数(複素数定数を構成する浮動小数点も同様)は、少なくとも256ビットの仮数と32ビットの符号付指数を表せること。
  • 整数定数を正確に表現できないときはエラーとすること。
  • 浮動小数点定数または複素数定数がオーバーフローしたために表せないときはエラーとすること。
  • 浮動小数点定数または複素数定数が精度による制限のために表せないときは近似値に丸めること。

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

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

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

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

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

個々の型(T)には、基礎になる型があります。Tが事前宣言済みの型、または型リテラルのとき、その型に対応する基礎型は、T自体です。それ以外のケースでは、Tの基礎型は、T型を宣言するときに参照した型の基礎型です。

   type T1 string
   type T2 T1
   type T3 []T1
   type T4 T3

string型である、T1T2の基礎型は、stringです。[]T1型である、T3T4の基礎型は、[]T1です。

メソッド群

型は、その型に関連付けられたメソッド群を持ちます。(§インタフェース型、§メソッドの宣言) インタフェース型のメソッド群はインタフェース自身です。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-ビット 浮動小数値

complex64   float32の実数部と虚数部を持つ複素数
complex128  float64の実数部と虚数部を持つ複素数

byte        uint8の別名(エイリアス)
rune        int32の別名(エイリアス)

「n-ビット整数」型の値はnビットのサイズを持ち、2の補数表現で表されます。

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

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

uint8の別名であるbyte型、およびint32の別名であるruneを除いて、数値型はサイズが同じであってもそれぞれが別の型です。これは移植時に問題が起きないようにするためです。異なる数値型どうしを式や代入で使用するときは変換が必要となります。たとえば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
[3][5]int
[2][2][2]float64  // [2]([2]([2]float64))と同じ

スライス型

スライスはある配列内の連続した領域への参照であり、スライスの内容はその配列の要素の並びです。スライス型は、その要素型と同じ要素型を持つ配列のすべてのスライスの集合を表します。初期化されていないスライス型の値はnilです。

SliceType = "[" "]" ElementType .

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

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

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

要素型がTである初期化済みの新しいスライスを作るには次のように組み込み関数makeを使用します。make関数はパラメータにスライスの型と長さ、オプションでキャパシティをとります。

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

makeの呼び出しは、隠された配列を新たに割り当て、それを参照するスライスを返します。

make([]T, length, capacity)

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

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

配列と同じく、スライスは常に一次元ですが、組み合わせて多次元を作ることができます。配列の配列を作成したとき、内側の配列の長さは常に同じですが、スライスのスライス(もしくはスライスの配列)では、長さは動的に変えることができます。このとき内側のスライスはmakeを使って個別に割り当てる必要があります。

構造体型

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

StructType     = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl      = (IdentifierList Type | AnonymousField) [ Tag ] .
AnonymousField = [ "*" ] TypeName .
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を持つ構造体
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をレシーバとする昇格されたメソッドを含みます。

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

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

ポインタ型

ポインタ型は所定の型(ベース型)の変数へのすべてのポインタの集合を表します。初期化されていないポインタ型の値は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)はすべてに記述するか、またはすべて省略するかどちらかです。記述したときはそれぞれの名前がアイテム(パラメータまたは戻り値)ひとつを表します。省略したときはそれぞれの型がその型のアイテムひとつを表します。パラメータと戻り値リストは常に丸括弧()でくくられます。例外として、戻り値がひとつだけで名前を持たないときだけは丸括弧なしで型だけを記述できます。

関数のシグネチャの最終パラメータは、型の前に…を伴うことができます。このようなパラメータを持つ関数は、可変引数関数として呼び出すことができ、このパラメータにゼロ個以上の引数を指定できます。

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" "{" { 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()
}

インタフェース型Tに自分自身、もしくは再帰的にTが埋め込まれている他のインタフェース型を埋め込むことはできません。

// 不正: 自分自身に埋め込めない
type Bad interface {
	Bad
}

// 不正: Bad2経由であってもBad1は自分自身を埋め込めない
type Bad1 interface {
	Bad2
}
type Bad2 interface {
	Bad1
}

マップ型

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

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

キーどうしの比較に使用するため、キー型で比較演算子==!=比較演算子)が完全に実装されている必要があります。そのためキー型には、関数型、マップ型、スライス型は許されません。キー型がインタフェース型のとき、比較演算子は動的なキー値を比較できなければなりません。比較できなければランタイムパニックとなります。

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

マップの要素数は、マップの長さ(length)と呼ばれます。例としてマップmの長さは組み込み関数len(m)を使用して調べることができ、この長さは実行中に変わります。マップ内の要素は代入を使って追加し、インデックス式を使って取り出します。削除には組み込み関数のdeleteを使用します。

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

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

キャパシティの初期値が指定したサイズを超えることはありませんが、マップ(nilマップを除く)は格納する要素に合わせてサイズを拡張します。nilマップは要素を格納できないこと以外は空のマップと同等です。

チャネル型

チャネルは同時に実行されている2つの関数に、同期実行と特定の要素型の値を受け渡す通信機構を提供します。初期化されていないチャネル型の値はnilです。

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

<-演算子で、チャネルの方向と、送信か受信かを指定します。方向が与えられなかったときは、チャネルは双方向になります。変換または代入によってチャネルは送信のみ、または受信のみ行うよう制約をかけられます。

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

<-演算子は、可能な限り最も左のチャネルと結びつきます:

chan<- chan int    // chan<- (chan int)と同じ
chan<- <-chan int  // chan<- (<-chan int)と同じ
<-chan <-chan int  // <-chan (<-chan int)と同じ
chan (<-chan int)

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

make(chan int, 100)

キャパシティは要素数であり、チャネルのバッファサイズです。キャパシティがゼロより大きいときはチャネルは非同期となります。非同期における通信操作は、バッファがいっぱいでないとき(送信時)、または空でない(受信時)ときは、ブロックすることなく即時に完了し、要素は送信された順序で受信されます。キャパシティがゼロもしくは未指定のときは、送信者と受信者がともに通信準備が整ったときにのみ通信が完了します。nilチャネルにおいて通信準備が整うことはありません。

組み込み関数closeを使ってチャネルをクローズできます。また、多値代入形式の受信演算子でチャネルがクローズ済みか調べられます。

型と値の特性

型の識別

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

2つの型の名前が同一のTypeSpecによるものであれば、これら2つの型は同一です。名前を持つ型と無名の型は常に異なる型です。2つの無名の型の、それぞれ対応する型リテラルが同じ(すなわち、同じリテラル構造を持ち、かつ対応する構成要素が同一の型)であれば、この2つは同一の型です。詳しく説明します。

  • 2つの配列型において、要素の型と配列の長さが同じであれば同一の型です。
  • 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, float64) *T0
	T5 func(x int, y float64) *[]string
)

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

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

T0T1が異なるのは、これらが別個に宣言された名前付きの型であるためです。「func(int, float64) *T0」と「func(x int, y float64) *[]string」は、T0[]stringが異なる型のため、同一ではありません。

代入可能

下のケースのとき値xは、T型の変数に対し代入可能(xは、Tに代入可能)です。

  • xの型が、Tと同一である。
  • xの型VTが同一の基礎型であり、さらに少なくともVまたはTどちらかが無名の型であれば同一である。
  • Tが、インタフェース型で、xT実装している。
  • xが、双方向チャネルの値で、Tもチャネル型であり、xの型VTが、同一の要素型を持ち、さらに少なくともVまたはTどちらかが名前付きでない。
  • xは、事前宣言済み識別子nilであり、Tがポインタ、関数、スライス、マップ、チャネル、インタフェース型のいずれかである。
  • xは、T型の値を表す型を持たない定数である。

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

ブロック

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

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

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

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

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

宣言とスコープ

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

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

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

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

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

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

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

ラベルのスコープ

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

ブランク識別子

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

事前宣言済み識別子

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

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

定数:
	true false iota

ゼロ値:
	nil

関数:
	append cap close complex copy delete imag len
	make new panic print println real recover

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

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

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

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

識別子のユニークさ

識別子群の中で、ある識別子がその識別子群内の他すべての識別子と異なっていればユニークであると言えます。2つの識別子のスペルが異なる、もしくは2つの識別子が異なるパッケージに記述されていてエクスポートもされていない場合、2つは異なる識別子です。それ以外は同一の識別子です。

定数の宣言

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

ConstDecl      = "const" ( 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 float32 = 0, 3    // u = 0.0, v = 3.0

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

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

iota

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

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 float64 = iota * 42  // v == 42.0  (float64の定数)
	w         = iota * 42  // w == 84    (型を持たない整数定数)
)

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

同一ExpressionList内ではiotaの値は常に同じです。これはiotaの値が各ConstSpecのあとにだけ増やされるためです。

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 | "(" { TypeSpec ";" } ")" ) .
TypeSpec     = identifier Type .
type IntArray [16]int

type (
	Point struct{ x, y float64 }
	Polar Point
)

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

type Block 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

// PtrMutexのベース型のメソッド群は、そのままだが、
// 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)
}

変数の宣言

変数は宣言によって作成され、識別子にバインドされ、型と初期値が与えられます。

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]  // マップからの引当。存在するかどうかだけ知りたい

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

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

変数に型が指定されず、かつ代入式の評価結果も型を持たない定数のとき、宣言された変数の型については§代入で説明します。

実装上の制約: 変数が関数の本体内で宣言されかつ一度も使用されない場合、コンパイラによって不正と判断されます。

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

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

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" FunctionName Signature [ Body ] .
FunctionName = identifier .
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() 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にバインドしています。

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

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

func(p *Point, factor float64)

しかし、このように宣言したとしても関数がメソッドになるわけではありません。

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

オペランド

オペランドは、式の基本要素である値です。オペランドに成りうるのは、リテラル、 定数または変数を表す識別子(限定付き識別子も可)、関数、関数を呼び出すメソッド式、カッコで括った式です。

Operand    = Literal | OperandName | MethodExpr | "(" Expression ")" .
Literal    = BasicLit | CompositeLit | FunctionLit .
BasicLit   = int_lit | float_lit | imaginary_lit | char_lit | string_lit .
OperandName = identifier | QualifiedIdent.

限定付き識別子

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

QualifiedIdent = PackageName "." identifier .

限定付き識別子は、異なるパッケージ(インポートが必要)内の識別子にアクセスするときに使用します。その識別子はエクスポートされていて、かつそのパッケージのパッケージブロックで宣言されていなければなりません。

math.Sin	// mathパッケージ内のSin関数を表している

複合リテラル

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

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

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

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

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

構造体を定義します。

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はゼロ値

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

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

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

var pointer *Point3D = &Point3D{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}

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

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{{1.5, -3.5}, {0, 0}}  // [...]*Point{&Point{1.5, -3.5}, &Point{0, 0}}と同じ

キーワードと「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, 2147483647}

// chが母音(vowel)のとき、vowels[ch]の値はtruevowels[ch]
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 = FunctionType Body .
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 |
	BuiltinCall |
	PrimaryExpr Selector |
	PrimaryExpr Index |
	PrimaryExpr Slice |
	PrimaryExpr TypeAssertion |
	PrimaryExpr Call .

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

セレクタ

セレクタは次の形式の基本式です。このxパッケージ名ではありません。

x.f

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

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

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

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

セレクタは自動的に構造体へのポインタの間接参照を行います。xが構造体へのポインタ型のときx.y(*x).yの簡略形として使用可能です。フィールドyも同じく構造体へのポインタ型のときx.y.zも同様に(*(*x).y).zの簡略形です。x*A型の匿名フィールドを持ち、かつ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.T0 != 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の要素型である
  • anilまたは、xが範囲外のときはランタイムパニックが発生する

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

  • xは整数値で、0 <= x < len(a)でなければならない
  • a[x]はインデックスxの位置にあるバイトであり、a[x]の型はbyte型である
  • a[x]には値を代入できない
  • xが範囲外のときはランタイムパニックが発生する

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

  • xの型はMのキー型に代入可能でなければならない
  • このマップがxをキーとするエントリを持っているとき、a[x]xをキーとするマップの値で、a[x]の型はMの値の型である
  • このマップがnilまたは、xをキーとするエントリを持っていないとき、a[x]Mの値の型のゼロ値である

これ以外のa[x]は不正となります。

次の形式を使い、map[K] V型であるマップaから代入や変数の初期化が行えます。

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

このインデックス式の結果として(V, bool)型の2つの値が返されます。キーxがマップ内に存在したときはokの値ははtrueで、存在しないときはfalseになります。vの値はv := a[x]とした時と同じです。

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

スライス

次の形式は文字列、配列、配列へのポインタ、スライスのいずれかであるaをスライスする基本式です。

a[low : high]

部分文字列またスライスが作成されます。結果として得たい要素をインデックス式のlowhighで指定します。得られる結果は0から始まるインデックスを持ち、長さはhighlowになります。次の例は配列aをスライスしています。

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

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

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

利便性のため、インデックス式はどちらも省略可能です。lowインデックスを省略したときのデフォルトはゼロで、highインデックスを省略したときのデフォルトはスライスしたオペランドの長さです。

a[2:]  // a[2 : len(a)]と同じ
a[:3]  // a[0 : 3]と同じ
a[:]   // a[0 : len(a)]と同じ

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

スライスする対象が文字列またはスライスのとき、スライスした結果は文字列もしくは同じ型のスライスになります。スライスの対象が配列のときは、アドレス指定可能でなければならず、スライスした結果は、その配列の要素型と同じ要素型を持ったスライスになります。

型アサーション

次の形式はインタフェース型の値xT型に型アサーションする基本式です

x.(T)

この式はxnilではなく、かつxにはT型の値が格納されていると断定します。このx.(T)という表記は、型アサーションと呼ばれます。

より明確にすると、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の戻り値の型となります。メソッドの実行も同様ではありますが、メソッドはそのメソッドのレシーバの型の値に対するセレクタで指定されます。

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

関数呼び出しにおいて、関数の値と引数は評価の順番に従い評価されます。評価後、呼び出しパラメータは関数に値渡しされ、呼び出された関数の実行が開始されます。関数から復帰したときに関数の戻りパラメータは呼び出し元の関数に値渡しで戻されます。

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

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

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

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

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

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

var p Point
p.Scale(3.5)

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

…パラメータの解析

fが、最終パラメータの型が...Tである可変引数関数であるとき、この引数は、関数内では、[]T型のパラメータと同じです。fの各呼び出しの際、最終パラメータに渡された引数は、[]T型の新しいスライスになり、その要素には実引数が順に格納されます。このときすべての値がT型に対し代入可能である必要があります。このスライスの長さは、最終パラメータに渡されたパラメータ数で、呼び出す側が異なれば長さも変わります。

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

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

Greeting関数内のwhoの値は[]string{"Joe", "Anna", "Eileen"}になります。

最後の引数が、スライス型[]Tに対し代入可能であり、引数の後ろに...を伴うときは、...Tパラメータの値は変更されることなく渡されます。この場合、新しいスライスは作成されません。

スライスsを使って呼び出してみます。

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

Greeting関数内のwhoの値は、sと同じ配列を参照する、同じ値になります。

演算子

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

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

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

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

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

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

シフト式の右側のオペランドは符号なし整数型か、もしくは符号なし整数型に変換可能な型を持たない定数でなければなりません。非定数シフト式の左側オペランドが、型を持たない定数のとき、その型はシフト式を左側オペランドだけに置き換えてみたときに得られる型となります。また、シフト式が型を持たない定数との比較に使われる場合のように、文脈から型が決まらないときはint型となります。

var s uint = 33
var i = 1<<s           // 1は、int型
var j int32 = 1<<s     // 1は、int32型; j == 0
var k = uint64(1<<s)   // 1は、int64型; k == 1<<33
var m int = 1.0<<s     // 1.0はint型
var n = 1.0<<s != 0    // 1.0はint型; int型のサイズが32ビットのとき、n == false
var o = 1<<s == 2<<s   // 1と2は、int型となる; int型のサイズが32ビットのとき、o == true
var p = 1<<s == 1<<33  // int型のサイズが32ビットのときNG: 1はint型だが、1<<33がオーバフロー
var u = 1.0<<s         // NG: 1.0は、float64型でシフト不可
var v float32 = 1<<s   // NG: 1は、float32型でシフト不可
var w int64 = 1.0<<33  // 1.0<<33は、定数のシフト式

演算子の優先順位

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

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

優先順位         演算子
    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 && <-chanPtr > 0

算術演算子

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

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

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

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

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

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

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

2つの整数xyにおける、商(q = x / y)と余り(r = x % y)は、下の関係を満たします。

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

x / yは、ゼロに近づくように切り捨て/切り上げられます。

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

この規則の例外は、被除数xがそのint型のサイズの最小値のとき、商(q = x / -1)はxと同一の値で、余りは0となります。

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

ゼロで除算したときは、ランタイムパニックが発生します。被除数が正の値で、除数が2の累乗の定数であるときは、その割り算は右シフトに置き換えられ、剰余の計算はビット演算のANDに置き換えられます。

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

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

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

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

浮動小数点または複素数においては、+xxと同じで、-xxの符号を反転させた値です。浮動小数点または複素数をゼロで除算したときの結果はIEEE-754では取り決められておらず、ランタイムパニックを起こすかどうかは実装に依存します。

整数のオーバフロー

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

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

比較演算子

比較演算子は、2つのオペランドを比較して、bool型を返します。

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

比較において、1番目のオペラランドは2番目のオペランドの型に対し代入可能でなければならず、またその逆も成り立たなくてはなりません。

等価演算子である==!=は、「比較可能」なオペランドに対して使用します。順序演算子である<<=>>=は「有順序」なオペランドに対して使用します。これらの用語および比較結果は次のように定義されています。

  • 論理値は「比較可能」です。2つの論理値はともにtrueであるか、もしくはともにfalseであれば等しいです。
  • 整数値は「比較可能」かつ「有順序」であり、一般法則に従い比較・順序付けされます。
  • 浮動小数点値は「比較可能」かつ「有順序」であり、IEEE-754の規定に従います。
  • 複素数値は「比較可能」です。2つの複素数値uとvはreal(u) == real(v)およびimag(u) == imag(v)を満たすとき等しいです。
  • 文字列の値は「比較可能」かつ「有順序」であり、バイト単位・辞書的に扱われます。
  • ポインタの値は「比較可能」です。2つのポインタの値が同じ変数を指し示すか、もしくはともに値がnilのとき等しいです。異なるゼロサイズ変数へのポインタは等しいときもあれば、等しくないときもあります。
  • チャネルの値は「比較可能」です。2つのチャネルの値は同一のmakeスライス、マップ、チャネルの作成)呼び出しによって作成されたとき、もしくは値がともにnilのとき等しいです。
  • インタフェースの値は「比較可能」です。2つのインタフェースの値は同一である動的な型と等しい値を持つか、もしくは値がともにnilであれば等しいです。
  • 非インタフェース型Xの値であるxと、インターフェイス型Tの値であるtは、X型の値が「比較可能」であり、かつXTを実装しているとき「比較可能」です。tの動的な型がXであり、かつtの動的な値がxと等しければお互い等しいです。
  • 構造体の値はすべてのフィールドが「比較可能」であれば「比較可能」です。2つの構造体の値は、対応する非ブランクフィールドがそれぞれ等しいときに等しいです。
  • 配列値は、配列の要素型の値が「比較可能」であれば「比較可能」です。2つの配列値は対応する要素がそれぞれ等しいときに等しいです。

2つのインタフェースの値の比較は、同一の動的な型を持っていてもその型の値が「比較可能」でなければランタイムパニックを起こします。この振る舞いは、インタフェースの値を直接比較するときだけでなく、インタフェース値の配列またはインタフェース値のフィールドを持つ構造体にもあてはまります。

スライス、マップ、関数の値は「比較可能」ではありません。ただし特殊ケースとして、スライス、マップ、関数の値は事前定義済み識別子であるnilとは「比較可能」です。ポインタ、チャネル、インタフェースの値とnilとの比較も可能で、そのときは上の原則に従います。

比較結果は論理値型に代入可能です。文脈からどの論理値型かを特定できないとき、結果の型はbool型になります。

type MyBool bool

var x, y int
var (
	b1 MyBool = x == y // 比較結果はMyBool型
	b2 bool   = x == y // 比較結果はbool型
	b3        = x == y // 比較結果はbool型
)

論理演算子

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

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

アドレス演算子

T型であるxをオペランドとするアドレス演算&xは、*T型であり、かつxを示すポインタを生成します。このオペランドはアドレス指定可能でなければなりません。すなわち、変数、ポインタの間接参照、スライスのインデックス操作、アドレス指定可能な構造体をオペランドとするフィールドセレクタ、アドレス指定可能な配列のインデックス操作です。このうち「アドレス指定可能」であることを条件としていても、x複合リテラルであれば例外として認められます。

*T型(ポインタ)であるxをオペランドとするポインタの間接参照*xは、xによって指されるT型の値を表します。xnilのとき、*xを評価しようとするとランタイムパニックが発生します。

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

受信演算子

チャネル型であるchをオペランドとする受信操作<-chの値は、チャネルであるchから受信した値であり、その値の型はチャネルの要素型です。この式は値が受信できるようになるまでブロックします。nilチャネルからの受信は永久にブロックされます。クローズされたチャネルからの受信は常に成功し、即座に要素型のゼロ値を返します。

v1 := <-ch
v2 = <-ch
f(<-ch)
<-strobe  // クロックパルスを待ち、受信した値を破棄する

受信式が、次の形式で代入または初期化で使用されるときは、追加情報が得られます。

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

追加情報として返されたbool型の値は、通信が成功したかどうかを示します。受信した値が、このチャネルへの送信操作が成功した結果として配信されたときはokの値がtrueとなり、チャネルがクローズされているか空であるためゼロ値が生成されたときはfalseとなります。

メソッド式

メソッド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 float32) float32 { 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 float32) float32

明示的にポインタのレシーバを指定することで、値のレシーバを持つメソッドを得ることができます。これは次のようになります。

(*T).Mv

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

func(tv *T, a int) int

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

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

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

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

変換

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

Conversion = LiteralType "(" Expression ")" .

型が演算子で開始するときは、括弧でくくらなければなりません:

*Point(p)        // *(Point(p)) と同じ
(*Point)(p)      // p は、(*Point) に変換される
<-chan int(c)    // <-(chan int(c)) と同じ
(<-chan int)(c)  // c は、(<-chan int) に変換される

次のケースのとき、定数xは型Tに変換可能です。

定数を変換すると、型を持つ定数となります。

uint(iota)               // iotaの値は、int型
float32(2.718281828)     // float32型の2.718281828
complex128(1)            // complex128型の1.0 + 0.0i
string('x')              // 文字列型の"x"
string(0x266c)           // 文字列型の"♬"
MyString("foo" + "bar")  // MyString型の"foobar"
string([]byte{'a'})      // 非定数: []byte{'a'} は、定数でない
(*int)(nil)              // 非定数: nilは、定数でなく、*intは、論理・数値・文字列型でない
int(1.2)                 // NG: 1.2は、int型で表現できない
string(65.0)             // NG: 65.0は、整数の定数でない

下のケースのとき、値xは型Tに変換可能です。

  • xは、T代入可能である。
  • xの型とTが、同一の基礎型である。
  • xの型とTが、無名のポインタ型で、それらのポインタのベース型が同一の基礎型を持つ。
  • xの型とTが、ともに整数型か浮動小数点型である。
  • xの型とTが、ともに複素数型である。
  • xが整数、または[]byte型か[]rune型であり、Tが文字列型である。
  • xが文字列で、T[]byte[]runeである。

数値型と文字列型の相互変換(非定数)にのみ適用されるルールがあります。これらの変換では、xが表現している値が変更される可能性があり、実行時のコスト増につながります。これ以外の変換では、型を変更するだけで、xの表現する値を変えることはありません。

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

整数型間の変換

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

  1. 整数型間の変換では、値が符号付き整数のときは、暗黙的に無限大の精度に符号拡張されます。符号なしのときは、ゼロ拡張されます。そのあとで変換結果となる型に一致するように切り捨てられます。たとえば、v := uint16(0x10F0)のときは、uint32(int8(v)) == 0xFFFFFFF0になります。変換の結果は常に有効な値となり、オーバフローは起こしません。
  2. 浮動小数点数を整数に変換するとき、小数部は捨てられます。(ゼロに近づくよう切り捨て/切り上げ)
  3. 整数、または浮動小数点の値を浮動小数点型に、もしくは複素数の値を他の複素数型に変換するときは、結果となる値はその変換先の型で規定されている精度に丸められます。たとえば、float32型の変数xの値は、格納されるときIEEE-754の32ビットの数値以上の精度が使われます。しかしfloat32(x)が表すのは32ビットに丸められたxの値です。同様に、x + 0.1は32ビット以上の精度が使われますが、float32(x + 0.1)はそうなりません。

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

文字列へまたは文字列からの変換

  1. 符号ありまたは符号なし整数値を変換することで、その整数が表すUTF-8文字を持つ文字列が得られます。有効なUnicodeコードポイントの範囲外の値は"\uFFFD"に変換されます。
    string('a')       // "a"
    string(-1)        // "\ufffd" == "\xef\xbf\xbd"
    string(0xf8)      // "\u00f8" == "ø" == "\xc3\xb8"
    type MyString string
    MyString(0x65e5)  // "\u65e5" == "日" == "\xe6\x97\xa5"
  2. バイトのスライスを文字列型へ変換することで、スライスの要素をバイト列として持つ文字列が得られます。スライスの値がnilであれば結果は空文字列になります。
    string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'})  // "hellø"
    
    type MyBytes []byte
    string(MyBytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'})  // "hellø"
  3. ルーンのスライスを文字列型へ変換することで、個々のルーンの値を文字列に変換し、それを連結した文字列が得られます。スライスの値がnilであれば結果は空文字列になります。
    string([]rune{0x767d, 0x9d6c, 0x7fd4})  // "\u767d\u9d6c\u7fd4" == "白鵬翔"
    
    type MyRunes []rune
    string(MyRunes{0x767d, 0x9d6c, 0x7fd4})  // "\u767d\u9d6c\u7fd4" == "白鵬翔"
  4. 文字列の値をバイト型のスライスへ変換することで、文字列のバイトデータを要素として持つスライスが得られます。文字列が空であれば結果は[]byte(nil)です。
    []byte("hellø")   // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    MyBytes("hellø")  // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
  5. 文字列の値をルーン型のスライスへ変換することで、文字列の各Unicodeコードポイントを持つスライスが得られます。文字列が空であれば結果は[]rune(nil)です。
    []rune(MyString("白鵬翔"))  // []rune{0x767d, 0x9d6c, 0x7fd4}
    MyRunes("白鵬翔")           // []rune{0x767d, 0x9d6c, 0x7fd4}

定数式

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

論理値型、数値型、文字列型定数がオペランドとして使用可能であるときは常に、型を持たない論理値、数値、文字列定数もそれぞれオペランドとして使用できます。シフト演算を除き、二項演算のオペランドが種類の異なる型を持たない定数のときの演算結果(非論理値演算)は整数、ルーン、浮動小数点、複素数のいずれか(後ろの方が優先度が高い)になります。たとえば、型を持たない整数定数を型を持たない複素数で割った結果は、型を持たない複素数定数となります。

定数の比較では、常に型論理値定数が得られます。シフト式定数の左側オペランドが型を持たない定数のとき、結果は整数の定数となりますが、それ以外は左側オペランドと同じ型の定数です。この左側オペランドは、整数型でなければなりません。(§算術演算子) 型を持たない定数に対し、他の演算子を適用した結果は、同種(すなわち、論理値・整数・浮動小数点・複素数・文字列定数)の型を持たない定数となります。

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.5   (float64型)
const d = 1 << 3.0         // d == 8     (型を持たない整数定数)
const e = 1.0 << 3         // e == 8     (型を持たない整数定数)
const f = int32(1) << 33   // f == 0     (int32型)
const g = float64(2) >> 1  // illegal    (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 (untyped complex constant)
const iΘ = complex(0, Θ)   // iΘ == 1.5i  (type complex128)

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

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となる

実装上の制約: コンパイラは型を持たない浮動小数点定数または複素数定数式の計算において丸め込みを行うことがあります。これについては定数セクションの実装上の制約を参照ください。精度が無限ではないため、この丸めこみによって浮動小数点定数の式が整数として正しくない値になることがあります。

評価の順番

式のオペランド代入returnステートメント、関数呼び出し、メソッド呼び出し、通信操作が評価される際には、字句が左から右へと順に評価されます。

代入例です。

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

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

a := 1
f := func() int { a = 2; return 3 }
x := []int{a, f()}  // xは[1, 3]または[2, 3]: aとf()の評価順は未定義

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

ステートメント

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

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

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

空ステートメント

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

EmptyStmt = .

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

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

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

式ステートメント

関数呼び出し、メソッド呼び出し、受信操作はステートメントのコンテキスト内に記述することができます。これらのステートメントは括弧で括れます。

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

送信ステートメント

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

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

チャネルと値の式は、ともに通信を開始する前に評価されます。送信が実行可能となるまで通信はブロックされます。バッファリングされていないチャネルへの送信は、受信側の準備ができたときに実行されます。バッファリングされているチャネルへの送信は、バッファに空きがあるときに実行されます。クローズ済みのチャネルへの送信はランタイムパニックを発生させます。nilチャネルへの送信は永久にブロックされます。

ch <- 3

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

「++」と「--」ステートメントは、型を持たない定数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  // k = <-chと同じ

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

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

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

x, y = f()

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

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

2番目の形式では、左辺のオペランドの数は右辺の式の数と一致し、かつ各右辺の式はすべて単一値とならなければなりません。右辺のn番目の式は左辺のn番目のオペランドに代入されます。代入は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}

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

ifステートメント

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

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

シンプルステートメント(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式の値と比較するための式を記述します。型switchcaseには、switch式にて特殊な形式で示された型と比較するための型を記述します。

式switch

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

ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
ExprCaseClause = ExprSwitchCase ":" { Statement ";" } .
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(); {  // switch式が空のときは、"true"と記述したことと同じ
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 ":=" ] PrimaryExpr "." "(" "type" ")" .
TypeCaseClause  = TypeSwitchCase ":" { Statement ";" } .
TypeSwitchCase  = "case" TypeList | "default" .
TypeList        = Type { "," Type } .

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

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

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

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

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

v := x  // xは一度だけ評価される
if v == nil {
	printString("x is nil")
} else if i, is_int := v.(int); is_int {
	printInt(i)  // iはint
} else if i, is_float64 := v.(float64); is_float64 {
	printFloat64(i)  // iはfloat64
} else if i, is_func := v.(func(int) float64); is_func {
	printFunction(i)  // iは関数
} 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はinterface{}
	} else {
		i := v
		printString("don't know the type")  // iは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 = Expression [ "," Expression ] ( "=" | ":=" ) "range" Expression .

range節の右側の式の型は、range式と呼ばれ、これは配列、配列へのポインタ、スライス、文字列、マップ、チャネルのいずれかでなければなりません。代入と同様に、左オペランドは、アドレス指定可能か、マップのインデックス式でなければなりません。(これらが、イテレーション変数です。) renage式がチャネルのときは、イテレーション変数は1つだけしか持てませんが、それ以外は、1つもしくは2つのイテレーション変数を持てます。イテレーションの2番目の変数にブランク識別子を指定したときのrange節は、2番目の変数しか指定しないrange節とまったく同じです。

range式は、ループの開始前に一度だけ評価されますが、式が配列の場合は、式によっては評価が行われないことがあります(後述)。 左辺の関数呼び出しは、イテレートするごとに一回ずつ評価されます。各イテレーションで、イテレーション値は下に示すように生成されます。

Range式                                   1番目の値                2番目の値(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                 要素          e  E
  1. aが配列、配列へのポインタ、スライス値のとき、要素インデックス0から始まる連番が、インデックスイテレーション値となります。特殊なケースとして、1番目のイテレーション変数のみ指定されたときは、rangeのループは0からlen(a)までのイテレーション値を生成するだけで、配列またはスライスの要素へはアクセスしません。nilスライスを指定した場合のイテレーション回数は0回です。
  2. 文字列の値のとき、「range」節は、文字列内のUnicodeコードポイントをバイトインデックス0から順番に繰り返します。イテレートすると、インデックス値は、文字列内のUTF-8エンコードされたコードポイントの先頭バイトのインデックスになります。2番目のrune型の値は、それに対応するコードポイントの値です。イテレーション時に不正なUTF-8シーケンスがあると、2番目の値は、0xFFFD(Unicode replacement character)となり、次の繰り返しのときに文字列内を1バイト進めます。
  3. マップをイテレートした時の処理順は未定義で、順番が次回も同じである保証はありません。イテレーション中に、まだ処理されていないエントリが削除されても、その値が処理されることはありません。イテレーション中にエントリが挿入されたときの振る舞いは実装に依存しますが、エントリが重複して処理されることはありません。nilマップを指定した場合のイテレーション回数は0回です。
  4. チャネルのときは、チャネルがクローズされるまで、そのチャネルに送信された値がイテレーション値となります。(§close) nilチャネルを指定した場合、range式は永久にブロックします。

イテレーション値は、代入ステートメントと同様に、対応するイテレーション変数に代入されます。

イテレーション変数は、「range」節内で省略形式(:=)を使用することで宣言可能です。このとき変数の型には、各イテレーション値の型がセットされ、そのスコープは「for」ステートメントの終わりまでとなります。変数は各イテレートで再利用されます。イテレーション変数が「for」ステートメント外で宣言されたときは、実行後の値は最後にイテレートされた時の値になります。

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
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]

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

goステートメント

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

GoStmt = "go" Expression .

この式は関数またはメソッドの呼び出しでなければなりません。ゴルーチンの呼び出しにおいて、関数値およびパラメータは通常通り評価されますが、通常の呼び出しと異なる点は、プログラムは実行された関数の完了を待たないことです。代わりに関数は新規のゴルーチンとして実行を開始します。関数終了時にゴルーチンも一緒に終了します。関数に戻り値があっても、それらは関数の完了時に破棄されます。

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

selectステートメント

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

SelectStmt = "select" "{" { CommClause } "}" .
CommClause = CommCase ":" { Statement ";" } .
CommCase   = "case" ( SendStmt | RecvStmt ) | "default" .
RecvStmt   = [ Expression [ "," Expression ] ( "=" | ":=" ) ] RecvExpr .
RecvExpr   = Expression .

RecvExprは、受信操作でなければなりません。「select」ステートメント内の全てのcaseにおいて、チャネル式が上から下へと順番に評価されます。このとき送信ステートメントの右側の式も一緒に評価されます。チャネルはnilであっても構いませんが、その場合はselectステートメント内にそのcaseが存在しないことと同等です。ただし送信の場合は、式の評価は行われます。selectステートメントの結果としてcaseがひとつ以上実行可能となると、その内のひとつが選ばれ、それが伴う通信処理とステートメントが評価されます。どれも実行可能とならないとき、defaultケースがあれば実行されますが、defaultケースがないときは通信のどれか1つが実行可能となるまでステートメントはブロックします。非nilチャネルのcaseが、ひとつもなければ、このステートメントは永久にブロックし続けます。たとえステートメントがブロックしても、チャネルと送信式は、selectステートメント内に入ってからは一度だけしか評価されません。

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

実行可能なcaseが複数あるときは、疑似乱数による平等な選択が行われ、実行される通信が決定されます。

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

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

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

select {}  // 永遠にブロック

returnステートメント

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

ReturnStmt = "return" [ ExpressionList ] .

戻り値の型を持たない関数内のreturnステートメントに戻り値を記述してはいけません。

func noResult() {
	return
}

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

  1. 戻り値として単一または複数の値がreturnステートメントで明示的にリストされます。個々の式は単一値であり、かつ対応する関数の戻り値の型に代入可能でなければなりません。
    func simpleF() int {
    	return 2
    }
    
    func complexF1() (re float64, im float64) {
    	return -7.0, -4.0
    }
  2. returnステートメントの式リストは、多値を返す関数の(一回の)呼び出しです。これは関数から返されたそれぞれの値が、適切な型を持ったテンポラリの変数に代入され、その変数がreturnステートメントにリストされるかのように振る舞います。そのあとは前のケースで説明した規則が当てはまります。
    func complexF2() (re float64, im float64) {
    	return complexF1()
    }
  3. 関数の戻り値のパラメータに名前をつけているときは、returnの式を空にすることができます(§関数型)。戻り値パラメータは、通常のローカル変数と同じで、関数内で必要に応じて値の代入ができます。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
    }

どう宣言されるかに関係なく、すべての戻り値は、関数が呼び出された直後に、それぞれの型のゼロ値で初期化されます。(§ゼロ値)

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 ] .

ラベルを記述するときは、そのラベルはforステートメントに付けられていなければなりません。すなわち、ラベルに対してcontinueすることは、該当するfor文の実行を進めることになります。(§forステートメント)

gotoステートメント

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

GotoStmt = "goto" Label .
goto Error

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

	goto L  // BAD
	v := 3
L:

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

ブロック外のgotoステートメントは、ブロック内のラベルにジャンプはできません。下の例などが相当します。

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

上の例は、ラベルL1forステートメントブロック内に在り、gotoはそのブロック内に無いためエラーとなります。

fallthroughステートメント

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

FallthroughStmt = "fallthrough" .

deferステートメント

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

DeferStmt = "defer" Expression .

deferに指定する式は関数またはメソッドの呼び出しでなければなりません。deferステートメントが実行される度に関数値と呼び出しパラメータは通常通り評価され、その結果は保存されますが、まだ関数の実行は行われません。代わりに保留された呼び出しは、deferステートメントを記述している関数からリターンする直前(戻り値があれば、その評価後)にLIFOの順序で実行されます。たとえば、遅延実行指定された関数が関数リテラルであり、deferを呼び出した関数が名前付きの戻り値パラメータを持つときは、この戻り値は関数リテラル内からもスコープを持つのでこの遅延実行指定された関数内で、この戻り値パラメータの値を(返却される前に)変更することができます。遅延実行された関数に戻り値があっても、それらは関数の完了時に破棄されます。

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

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

// f returns 1
func f() (result int) {
	defer func() {
		result++
	}()
	return 0
}

組み込み関数

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

この組み込み関数は、Go言語で規定されているどの型とも異なるので、呼び出し式でのみ使われます。関数値として使用することはできません。

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

close

cがチャネルのとき、組み込み関数close(c)はこのチャネルに今後値が送信されないよう印をします。cが受信専用チャネルであったときはエラーとなります。クローズ済みのチャネルに対し送信またはクローズしようとするとランタイムパニックを引き起こします。またnilチャネルをクローズしたときもランタイムパニックを引き起こします。closeを呼び出し、事前に送信された値をすべて受信し終えた後で受信操作を行うと、そのチャネル型のゼロ値がブロックされることなしに返されます。多値を受け取る受信操作では、受信した値とともにチャネルがクローズされているかを示す値を返します。

長さ、キャパシティ

組み込み関数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)

nilであるスライス、マップ、チャネルの長さ、およびキャパシティは0です。

sが文字列定数のとき、式len(s)定数です。sが配列または配列へのポインタで、式s内にチャネルからの受信または関数呼び出しが含まれていなければ、式len(s)およびcap(s)は定数であり、sは評価されません。これらが含まれているときは、lencapは定数ではなく、sは評価されます。

メモリの割当

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

new(T)

例です。

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

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

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

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

呼び出し          型T        結果

make(T, n)       slice      長さn、キャパシティnであるT型のスライス
make(T, n, m)    slice      長さn、キャパシティmであるT型のスライス

make(T)          map        T型のマップ
make(T, n)       map        要素の初期容量がnであるT型のマップ

make(T)          channel    T型の同期チャネル
make(T, n)       channel    バッファサイズがnであるT型の非同期チャネル

引数nmは、整数型でなければなりません。nがマイナス値であるかmより大きいとき、もしくはnまたはmint型で表現できないときはランタイムパニックが発生します。

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要素を初期容量として持つマップ

スライスの追加とコピー

スライスの操作に役立つ、2つの組み込み関数があります。

可変引数関数であるappend関数は、ゼロ個以上の値であるxS型のスライスsに追加し、結果として同じS型のスライスを返します。xの値は...T型のパラメータとして受け渡されます。このTS要素型で、各値にパラメータ受け渡し規則が適用されます。例外的に、appendは最初の引数に[]byte型、2番目の引数に…を後ろに伴う文字列型を受け付けます。この形式は文字列内のバイトデータを追加します。

append(s S, x ...T) S  // Tは、Sの要素型

sのキャパシティが、追加する値を格納するに充分な大きさを持たないとき、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}

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へスライスの要素をコピーします。コピー元とコピー先の重複は許されます。両引数の要素型が同じT型で、[]T型のスライスに代入可能である必要があります。コピーされる要素数はlen(src)len(dst)の短い方が使われます。特例として、copyは、引数のコピー先に[]byte型、コピー元に文字列型を受け付けます。この形式は、文字列をbyteスライスにコピーします。

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

例:

var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
var s = make([]int, 6)
var b = make([]byte, 5)
n1 := copy(s, a[0:])            // n1 == 6, s == []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")

マップ要素の削除

組み込み関数deleteは、キーkを使ってマップmから要素を削除します。kの型はmのキー型に対し代入可能でなければなりません。

delete(m, k)  // マップmから要素m[k]を削除

要素m[k]が存在しないとき、deleteは何も行いません。nilマップに対しdeleteを呼び出すとランタイムパニックを引き起こします。

複素数の操作

複素数の作成と分解を行う3つの関数が用意されています。組み込み関数のcomplexは、浮動小数の実数と虚数部分から複素数の値を作成します。realimagは複素数の値から、それぞれ実数と虚数部分を取り出します。

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

引数の型と戻り値の型は対応しています。complexの場合は、2つの引数は同一の浮動小数の型であり、その戻り値の型は引数の浮動小数の型と対応する複素数型です。対応の組みわせは、complex64float32complex128float64です。realimag関数にもこれと逆があてはまります。ゆえに、「z, z == complex(real(z), imag(z))」となります。

これらの関数のオペランドがすべて定数であるとき、戻り値も定数となります。

var a = complex(2, -2)             // complex128
var b = complex(1.0, -1.4)         // complex128
x := float32(math.Cos(math.Pi/2))  // float32
var c64 = complex(5, -x)           // complex64
var im = imag(b)                   // float64
var rl = real(c64)                 // float32

パニックの制御

組み込み関数のpanicrecoverは、ランタイムパニックおよびプログラム定義のエラーにおける、状態の報告と制御を手助けします。

func panic(interface{})
func recover() interface{}

関数Fの中でpanicを呼び出すとFの実行はすぐに停止します。F関数内でdeferにより遅延指定された関数があれば実行された後、Fは呼び出し元に復帰します。Fの呼び出し元側では、F関数を呼び出した箇所があたかもpanic関数を呼び出したかのように振舞い、実行を停止し遅延指定した関数を実行します。この連鎖はゴルーチン内の全ての関数が実行を停止するまで、呼び出しとは逆順に続けられます。最後にプログラムが終了し、panicに渡された引数の値とともにエラーの状態が報告されます。この終了シーケンスは「パニックする」「パニクる」と呼ばれます。

panic(42)
panic("unreachable")
panic(Error("cannot parse"))

recover関数を使うとプログラム側でパニック中のゴルーチンを制御できます。遅延起動した関数内でrecoverを呼び出す(それ以外の関数は呼び出さない)と停止した実行を再開することでパニックシーケンスを停止し、panicの呼び出しの際に渡されたエラー情報が返されます。遅延起動された関数以外でrecoverを呼び出しても、パニックシーケンスは中断することはありません。遅延されていないとき、またはゴルーチンがパニック中でないとき、またはpanicに与えられた引数がnilのとき、recovernilを返します。

次の例の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()
}

ブートストラッピング

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

関数        振舞い

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

パッケージ

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

ソースファイルの構成

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

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

パッケージ節

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

PackageClause  = "package" PackageName .
PackageName    = identifier .

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

package math

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

インポート宣言

インポート宣言は、ソースファイル内に含まれている宣言がインポートしたパッケージ(§プログラムの初期化と実行)が持つ機能に依存していることを表し、かつそのパッケージでエクスポートされている識別子へのアクセスを可能にします。インポート名はアクセスに使用する識別子(PackageName)であり、ImportPathにはインポートされるパッケージを指定します。

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

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

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

実装上の制約: コンパイラはImportPathsにUnicodeの一般カテゴリL、M、N、P、Sに所属する文字(絵文字、空白を除く)だけを使った非空文字列の使用を制限します。同様に次の文字 !"#$%&'()*,:;<=>?[\]^`{|} および Unicode replacement character(U+FFFD)も除きます。

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 float64; 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を参照している別のパッケージで定義されている関数を呼び出したときの結果は規定されていません。

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

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

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

mainパッケージと呼ばれる、他からインポートされることがないパッケージをリンクすることでプログラムは完成します。このmainパッケージから全てのパッケージが直接・間接的にインポートされます。mainパッケージは、パッケージ名はmainであり、かつ引数と戻り値を持たないmain関数を宣言していなければなりません。

func main() { … }

プログラムの実行はmainパッケージを初期化したあと、main関数を実行することで開始します。main関数から抜けるとプログラムは終了します。このとき、他(main以外)のゴルーチンの終了待ちは行いません。

パッケージの初期化(変数の初期化およびinit関数の実行)は、ひとつのゴルーチンを使って順番に、ひとパッケージづつ行われます。init関数が別のゴルーチンを起動させれば初期化コードを並列動作させられます。しかし、初期化においてinit関数は常に順番に実行されるので、前のinit関数から復帰するまで次のinit関数が開始されることはありません。

error

事前宣言済みの型、errorは次のように定義されています。

type error interface {
	Error() string
}

これは、エラー状態を表すのに便利なインタフェースであり、nil値はエラーがない状態を表します。たとえばファイルからデータを読み出す関数は、次のように定義します。

func Read(f *File, b []byte) (n int, err error)

ランタイムパニック

配列に範囲外のインデックスを指定したときに発生するような実行エラーは、ランタイムパニックの引き金となります。これは組み込み関数panicをインタフェース型のruntime.Errorの実装した値で呼び出したことと同じです。この型は事前定義済みのインタフェース型errorを満たしています。ランタイムエラーの状態を個別に表すような厳密なエラー値は規定されていません。

package runtime

type Error interface {
	error
	// and perhaps other methods
}

システム考察

unsafeパッケージ

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

package unsafe

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

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

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

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

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

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

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

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

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

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

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

型                                   サイズ(バイト数)

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

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

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

構造体型および配列型は、サイズが1以上のフィールド(または要素)を持たなければ、そのサイズはゼロです。サイズがゼロである2つの異なる変数は、メモリ上のアドレスが同一となる場合があります。