このドキュメントは、http://golang.org/doc/go_spec.htmlの翻訳です。
Version of June 7, 2010
はじめに
この文書は、プログラミング言語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回)
イコールの左辺の名称が小文字の場合は、その名称はトークンを識別するために使われます。非終端は大文字と小文字を混在させて記述します。シンボルはダブルクォート""、またはバッククォート``でくくられます。
「a ... b」形式はaからbまでのうちの、いずれかの文字のセットを表します。
ソースの文字コード
ソースの文字コードは、UTF-8でエンコードされたUnicode文字です。テキストは正規化されないため、あるアクセント付きコードポイントは、それと同じ文字ではあるがアクセント記号とアルファベットを結合して造られた文字とは別のものとして扱われます。これらは2つのコードポイントとみなされます。このドキュメントでは「Unicodeコードポイント」と記述する代わりに、文字(キャラクタ)という言葉を使用します。
各コードポイントは、それぞれ異なる文字です。たとえば、同じアルファベットでも大文字・小文字が違えば別の文字として扱われます。
実装上の制限:他のツールとの整合性のため、ソーステキスト中のNUL文字(U+0000)は許可されません。
文字
次の書き方は、特定のUnicode文字クラスを示すときに用いられます。
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を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種類の書式があります。
- 行コメントは、文字シーケンス
//で始まり、次の改行までがコメントとなります。行コメントは改行として扱われます。 - 一般的な形式のコメントは、文字シーケンス
/*で始まり、文字シーケンス*/までがコメントとなります。複数行に渡るコメントは改行として扱われ、それ以外はスペースとして扱われます。
コメントを入れ子にすることはできません。
トークン
トークンは、Go言語のボキャブラリを形成し、これには識別子、キーワード、演算子および区切り文字、リテラルの4つのクラスがあります。ホワイトスペースはスペース(U+0020)、水平タブ (U+0009)、キャリッジリターン (U+000D)、改行 (U+000A)です。ホワイトスペースは、それがなければ一つのトークンとして結合されてしまうトークンを分割するために使う他は無視されます。また、改行はセミコロンが自動挿入されるトリガとなります。コンパイラなどがソースコードの解析でソースコードをトークンに分割するとき、その次のトークンは長い文字列シーケンスから成る有効なトークンです。
セミコロン
正式な文法としては、ステートメントの終端としてセミコロン";"を使います。ただしGo言語のプログラムでは下の2つの規則を用いてセミコロンの大部分を省略できます:
- コンパイラがソースコードをトークンに分割するとき、非空白行の行末のトークンが下のいずれかと一致すれば、セミコロンが自動挿入されます。
- 識別子
- 整数、浮動小数点、文字、虚数、文字列リテラル
- 次のキーワード
break、continue、fallthrough、return - 次の演算子およびデリミタ
++、--、)、]、}
- 一行に複数のステートメントを記述するときでも閉括弧
")"または"}"直前のセミコロンは省略できます。
このドキュメント内のサンプルコードでは、慣用的な使用方法を示すためにこれらの規則に従ってセミコロンを省いています。
識別子
識別子は、変数や型といったプログラムの実体に対し名前をつけます。
識別子は、一文字以上の字(レター)と数字から構成されます。先頭一文字は字でなくてはなりません。
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~f、A~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コードポイントを表現します。クォート内にはシングルクォートと改行を除くどんな文字も記述できます。バックスラッシュを使用したマルチ文字シーケンスの各書式を使ってエンコードすることで、シングルクォート文字自体も表せます。
クォートを使うと簡単に一文字を表すことができます。これは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 (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進数(\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 } "`" . 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"
次の例は、すべて同じ文字列を表しています。
"日本語" // 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、複素数定数のrealとimag、数値定数のcmplxによって表されます。ブーリアンの真理値は事前宣言済み定数trueおよびfalseによって表されます。事前宣言済み識別子iotaは整数リテラルを表します。
一般的に、複素数定数は定数式の一形式であるため、そのセクション内で解説します。
数値定数は、任意精度の値を表し、オーバーフローすることがありません。
定数には、型を持つものと持たないものがあります。リテラル定数、true、false、iota、および「定数式内の全オペランドが型を持たない定数であるとき」は、定数は型を持ちません。
定数は、定数の宣言または変換によって明示的に、または変数の宣言・代入・式のオペランドで使われるときに暗黙的に型が付与されます。ただし定数の値が、各型の値を正確に表現することができなければエラーとなります。例えば3.0はすべての整数型もしくは浮動小数点型にすることができます。しかし2147483648.0(1<<31と同値)はfloat32型、float64型、uint32型にはできますが、int32型やstring型にはできません。
IEEE-754の無限値と非数値を表す定数はありませんが、実行時にmathパッケージのInf、NaN、IsInf、IsNaN関数を使ってこれらの値を扱うことができます。
実装の制約:コンパイラは数値定数の内部表現に最小でもマシンの倍のビット数を使います。浮動小数点の値については、仮数部と指数部それぞれが倍になります。
型
型は、その型を持つ値に対し具体的な値と操作の組み合わせを規定します。型を表すには型の(たいていパッケージ名を伴なう)名称(§限定付き識別子、§型の宣言) または型リテラルで記述します。事前に宣言した型から別の新しい型を作成することもできます。
Type = TypeName | TypeLit | "(" Type ")" . TypeName = 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型である、T1とT2の基礎型は、stringです。[]T1型である、T3とT4の基礎型は、[]T1です。
型は、その型と関連付けられたメソッド群を持ちます (§インタフェース型、§メソッドの宣言) 。インタフェース型のメソッド群はインタフェース自身です。インタフェース以外の型を仮に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-ビット 浮動小数値 complex64 float32の実数部と虚数部を持つ複素数 complex128 float64の実数部と虚数部を持つ複素数 byte uint8の別名
「n-ビット 整数」型の値はnビットのサイズを持ち、2の補数表現で表されます。
実装に依存したサイズを持つ数値型も用意されています。
uint 32 または 64 ビット int 32 または 64 ビット float 32 または 64 ビット complex 実数と虚数部に浮動小数点型を持つ uintptr ポインタの値をそのまま格納するのに充分な大きさの符号なし整数
uint8の別名であるbyte型を除いて、数値型はサイズが同じであってもそれぞれが別の型です。これは移植時に問題が起きないようにするためです。異なる数値型どうしを式や代入で使用するときは変換が必要となります。たとえばint32とintが、あるアーキテクチャ上で同一のサイズであったとしてもこれらは同じ型ではありません。
文字列型
文字列型は文字列の値を表現します。文字列は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)で調べることができます。len()とcap()の関係は次のとおりです。
0 <= len(a) <= cap(a)
このnilスライスの長さとキャパシティはともに0です。要素型が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 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(prefix string, values ...int)
func(a, b int, z float) bool
func(a, b int, z float) (bool)
func(a, b int, z float, opt ...interface{}) (success bool)
func(int, int, float) (float, *[]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()
}
インタフェースは複数の型に実装することができます。例えば、S1とS2の2つの型が次のメソッド群を持つ場合、
func (p T) Read(b Buffer) bool { return ... }
func (p T) Write(b Buffer) bool { return ... }
func (p T) Close() { ... }
(TがS1かS2を表すとして)FileインタフェースはS1とS2両方に実装されます。S1とS2が他のメソッドを持つか共有していても関係ありません。
型は、その型が持つメソッド群の一部で構成されているインタフェースすべてを実装していることになります。したがって複数の異なるインタフェースを実装することもできます。例を挙げると、すべての型は次の空(empty)インタフェースを実装しています。
interface{}
同様に、下のインタフェースの記述方法をご覧ください。ここでは型の宣言でLockという名のインタフェースを定義しています。
type Lock interface {
Lock()
Unlock()
}
S1とS2がこれらのメソッドを実装した場合、
func (p T) Lock() { ... }
func (p T) Unlock() { ... }
S1、S2はFileインタフェースと同様に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)を使用して調べることができ、この値は実行時に変化します。マップ内の要素は特殊な代入方法で実行中に追加、削除できます。
空の新しいマップを作るには、組み込み関数makeを使用します。make関数は引数にマップの型と、オプションでキャパシティのヒント値をとります。
make(map[string] int) make(map[string] int, 100)
キャパシティの初期値は指定したサイズを超えることはありませんが、マップは格納する要素が収まるようにサイズを拡張します。
チャネル型
チャネルは同時に実行されている2つの関数に、同期実行と特定の要素型の値を受け渡す通信機構を提供します。初期化されていないチャネル型の値はnilです。
ChannelType = ( "chan" [ "<-" ] | "<-" "chan" ) ElementType .
<-演算子で、チャネルの方向と、送信か受信かを指定します。方向が与えられなかったときは、チャネルは双方向になります。変換または代入によってチャネルは送信のみ、または受信のみ行うよう制約をかけられます。
chan T // T型の値を送受信可能 chan<- float // floatの送信のみ <-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)
キャパシティは要素数であり、チャネルのバッファサイズです。キャパシティがゼロより大きいときはチャネルは非同期になり、バッファがいっぱいになるまで送信はブロックすることなく成功します。キャパシティがゼロまたは指定しなかったときは送信・受信側双方が準備ができたときだけ通信が実行されます。
組み込み関数closeを使ってチャネルのクローズ、およびclosedを使ってクローズされているか確認することができます。
型と値の特性
型の識別
2つの型は、「同一」か「異なる」かのどちらかです。
2つの型の名前が同一の宣言によるものなら、これら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, 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)
T0とT1が異なるのは、これらが別個に宣言された名前付きの型であるためです。「func(int, float) *T0」と「func(x int, y float) *[]string」は、T0と[]stringが異なる型のため、同一ではありません。
代入可能
下のケースのとき、値xは、T型の変数に対し代入可能(xは、Tに代入可能)です。
-
xの型が、Tと同一である。 -
xの型VまたはTの基礎型が同一であり、VまたはTが無名の型である。 -
Tが、インタフェース型で、xはTを実装している。 -
xが、双方向チャネルの値で、Tもチャネル型であり、xの型VとTは、同一の要素型を持ち、さらにVまたはTが名前付きでない。 -
xは、事前宣言済み識別子nilであり、Tがポインタ、関数、スライス、マップ、チャネル、インタフェース型のいずれかである。 -
xは、T型の値を表す、型を持たない定数である。
Tが構造体のときは、Tの全フィールドがエクスポートされているか、またはTが宣言されているパッケージ内で代入が行われる必要があります。言い換えると構造体型の値を構造体型の変数に代入できるのは、構造体の全フィールドがプログラムから個別に代入可能なときだけです。
また、ブランク識別子にはどんな値でも代入可能です。
ブロック
ブロックは、対になる波括弧{}内の一連の宣言とステートメントです。
Block = "{" { Statement ";" } "}" .
ソースコード内には明示的なブロックの他に、下に示す潜在的なブロックがあります。
- すべてのGo言語ソースを包括する、ユニバースブロック。
- 各パッケージ内の全Go言語ソースを包括する、パッケージブロック。
- 各ファイル内の全Go言語ソースを包括する、ファイルブロック。
- 各
if、for、switchステートメントは、それ自身が潜在的なブロックであるとみなされます。 switchとselectステートメント内の節は、潜在的なブロックの働きをします。
ブロックはネストし、スコープに影響します。
宣言とスコープ
宣言をおこなうことによって識別子(ブランク識別子を除く)は、定数、型、変数、関数、パッケージにバインドされます。プログラム内のすべての識別子は宣言されていなければなりません。識別子は同じブロック内で2回宣言されることはありません、また識別子をファイルブロックとパッケージブロックの両方で宣言することはできません。
Declaration = ConstDecl | TypeDecl | VarDecl . TopLevelDecl = Declaration | FunctionDecl | MethodDecl .
スコープとはソース内で宣言済み識別子に割り当てられた定数、型、変数、関数、パッケージを参照可能な範囲のことです。
Go言語ではブロックを使ってスコープ制御を行います。
- 事前宣言済み識別子が持つスコープは、ユニバースブロックです。
- トップレベル(関数外)にて宣言されている定数、型、変数、関数の識別子が持つスコープは、パッケージブロックです。
- インポートされたパッケージの識別子が持つスコープは、そのインポート宣言を含んでいるファイルブロックです。
- 関数のパラメータまたは戻り値として宣言されている変数の識別子が持つスコープは、関数の本体部分です。
- 関数内で宣言されている定数または変数の識別子が持つスコープは、ConstSpecまたはVarSpecの直後から始まり、その識別子を含んでいるブロックの終わりまでです。
- 関数内で宣言している型の識別子が持つスコープは、TypeSpecの識別子から始まり、その識別子を含んでいるブロックの終わりまでです。
ブロック内で宣言された識別子は、内側のブロック内で再宣言できます。内側のブロック内で宣言した識別子がスコープ内にある間、その識別子は内側で宣言した実体を表しつづけます。
パッケージ節は宣言ではありません。パッケージ名がどのスコープにも現れることがないからです。パッケージ節は同一のパッケージに所属するファイルを識別するためと、インポートされるときにデフォルトとして使われるパッケージ名を定義するためのものです。
ラベルのスコープ
ラベルはラベルステートメントによって宣言され、break、continue、gotoステートメント内で使用されます(§Break ステートメント、§Continue ステートメント、§Goto ステートメント)。ラベルはその他の識別子と異なりブロックによるスコープ制御は行われず、ラベル以外の識別子との重複が許されます。ラベルのスコープはそのラベルが宣言されている関数の本体内ですが、ネストした関数はスコープ外です。
事前宣言済み識別子
下に示す識別子は、ユニバースブロック内で明示的に宣言されています。
基本型: bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 アーキテクチャ仕様に従う型: complex float int uint uintptr 定数: true false iota ゼロ値: nil 関数: cap close closed cmplx copy imag len make new panic print println real
エクスポートされた識別子
識別子は、他パッケージからの限定付き識別子を使ったアクセスを許すためエクポートされることがあります。識別子は下の2つの条件を満たすときにエクスポートされます。
- 識別子の名前の最初の一文字が大文字(Unicodeクラス”Lu”)である。なおかつ
- 識別子がパッケージブロック内で宣言されている、またはパッケージブロック内で宣言された型のフィールドかメソッドである。
これ以外の識別子がエクスポートされることはありません。
ブランク識別子
アンダースコア(_)を使って記述されるブランク識別子は、他の識別子と同様に宣言で使用されますが、その宣言ではバインドは行われません。
定数の宣言
定数の宣言では、識別子(定数名)のカンマ区切りのリストを、定数式のリストにバインドします。識別子と式の数は一致している必要があり、左辺の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 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にリセットされ、各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 float = iota * 42 // v == 42.0 (浮動小数点定数) 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 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
}
// MyCipherは、Cipherと同じメソッド群を持つインタフェース型です。 type MyCipher Cipher
型の宣言は、論理値型、数値型、文字列型から新たな型を定義してメソッドを追加するために使われることがあります。
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 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] // マップからの引当。存在するかどうかだけ知りたい
式リストが与えられたときは、変数にその式が順番に代入 (§代入)され初期化されます。そのとき式リスト内のすべての式が代入で使用され、また全ての変数がそれによって初期化されなければなりません。式リストが与えられないときは各変数はゼロ値で初期化されます。
型が指定されると変数はその型になります。指定されないときは代入された式から型が推測されます。
型が指定されず、かつ対応する代入された式が型を持たない定数のとき、宣言された変数の型は、bool、 int、 float、 stringのいずれかになります。どの型になるかは値に依存します。
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の再宣言
省略形式は関数内でのみ使用されます。また省略形式はif、for、switchステートメントのイニシャライザで一時的なローカル変数を宣言するためにも用いられます(§ステートメント)。
関数の宣言
関数の宣言は、識別子と関数をバインドします(§関数型)。
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
}
レシーバ型が*PointであるLengthメソッドとScaleメソッドを、ベース型Pointにバインドしています。
レシーバの値がメソッドの本体部で参照されないのであれば、宣言時にレシーバの識別子を省略可能です。同じことは一般的に関数とメソッドのパラメータにもあてはまります。
メソッドの型は第一引数にレシーバを伴った関数の型となります。例えばScaleメソッドは次の型になります。
(p *Point, factor float)
しかし、このように宣言したとしても関数がメソッドになるわけではありません。
式
式は値の算出方法を規定します。値の算出はオペランドに演算子や関数を適用することで行われます。
オペランド
オペランドは、式の基本要素である値です。
Operand = Literal | QualifiedIdent | MethodExpr | "(" Expression ")" . Literal = BasicLit | CompositeLit | FunctionLit . BasicLit = int_lit | float_lit | imaginary_lit | char_lit | string_lit .
限定付き識別子
限定付き識別子とはパッケージ名をプレフィックスとして指定した識別子で、これにはブランク識別子は使用できません。
QualifiedIdent = [ PackageName "." ] identifier .
限定付き識別子は、他のパッケージ内の識別子にアクセスするときに使用します。その識別子はエクスポートされていなければなりません。すなわち識別子がUnicodeの大文字で始まっている必要があります。
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となります。
セレクタには次の規則が適用されます。
- 仮に
T型もしくは*Tである値xがあり、Tがインタフェース型でなく、該当するフィールドまたはメソッドfが存在するならば、x.fが表すのはT内で深さの値が最も小さいフィールドまたはメソッドfです。最も浅い深さのfがひとつだけでなければこのセレクタは不正となります。 - 仮に
I型もしくは*Iである値xがあり、Iがインタフェース型で、かつ該当するメソッドが存在するならば、x.fが表すのはxに割り当てられている値が持っているfという名前の実メソッドです。xに値がないかnilのとき、x.fは不正となります。 - これら以外のケースでは、
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の要素型である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の値の型である - このマップが
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]とした時と同じです。
同様にマップに代入するときは、次の特殊な形式を使うことができます。
a[x] = v, ok
このとき論理値であるokの値がfalseであれば、キーがxであるエントリはマップから削除されます。okの値がtrueであれば普通の代入と同じようにマップに要素が代入されます。
スライス
次の形式は文字列、配列、スライスのいずれかであるaをスライスする基本式です。
a[lo : hi]
部分文字列またスライスが作成されます。結果として得たい要素をインデックス式のloとhiで指定します。得られる結果は0から始まるインデックスを持ち、長さはhi – loになります。次の例は配列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
利便性のため、hi式は省略可能で、a[lo :]はa[lo : len(a)]の省略形です。配列または文字列のインデックスloとhiは「 0 <= lo <= hi <= 長さ」を満たしていなくてはなりません。スライスからスライスを作成するときは、上限値は長さではなくキャパシティとなります。
スライスする対象が文字列またはスライスのとき、スライスした結果は文字列もしくは同じ型のスライスになります。スライスの対象が配列のときはスライスした結果は、その配列の要素型と同じ要素型を持ったスライスになります。
型アサーション
次の形式はインタフェース型の値xをT型に型アサーションする基本式です
x.(T)
この式はxはnilではなく、かつ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)を返します。このZはT型のゼロ値です。このときはランタイムパニックは発生しません。このときの型アサーションは、値と成否を返す関数を呼び出したのと同様に振舞います。 (§代入)
呼び出し
下は、F型の関数であるfの式です。
f(a1, a2, ... an)
これは引数、a1, a2, ... anを伴なうfの呼び出しです。1つの特例を除き、各引数は単一値となる式であり、その値はFのパラメータの型と代入可能である必要があり、これらの引数の式は関数の呼び出し前に評価されます。この式の型はFの戻り値の型となります。メソッドの実行も同様ではありますが、メソッドはそのメソッドのレシーバの型の値に対するセレクタで指定されます。
math.Atan2(x, y) // 関数の呼び出し var pt *Point pt.Scale(3.5) // レシーバptによるメソッドの呼び出し
特例として、関数またはメソッド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.Crash("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型の新しいスライスになり、その要素には実引数が順に格納されます。このスライスの長さは、最終パラメータに渡されたパラメータ数で、呼び出す側が異なれば長さも変わります。
関数とその呼び出しです。
func Greeting(prefix string, who ... string)
Greeting("hello:", "Joe", "Anna", "Eileen")
Greeting関数内の whoの値は[]string{"Joe", "Anna", "Eileen")になります。
特例として、関数が受け取った…パラメータを、同じ型の...パラメータを持つ他の関数の...引数として受け渡すとき、このパラメータは、そのまま渡されます。すなわち、型が同じであれば、...仮パラメータは変更されることなく...実パラメータとして受け渡されます。
演算子
演算子はオペランドを伴って式を作ります。
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 << 1 はx*2と同じであり、x >> 1は x/2を負の無限大の値に近づくように切り捨てた値と同じになります。
整数オペランドに対する単項演算子+、-、^は以下で示すように定義されています。
+x は、0 + x
-x 符号反転 は、0 - x
^x ビットの補数 は、m ^ x (xが符号なしのとき、mの全ビットは1。
xが符号ありのとき、mは-1)
浮動小数点においては、+xはxと同じで、-xはxの符号を反転させた値です。浮動小数点をゼロで除算したときの結果はIEEE-754では取り決められておらず、ランタイムパニックを起こすかどうかは実装に依存します。
整数のオーバフロー
符号なし整数において、+、 -、*、<<演算子はmodulo 2n (nは、この符号なし整数型のビット幅)で計算されます(§数値型)。大雑把な解説をすると、これら符号なし整数の演算は、オーバフローした高位のビットを破棄するので、プログラム側はこの「ラップアラウンド」が行われることを期待してもよいでしょう。
符号あり整数において、+、 -、*、<<演算子はオーバフローを起こすことがあり、演算結果の値として何が返されるかは、この符号あり整数の値、演算子、オペランドにより決まります。オーバフローが起きても例外は発生しません。オーバフローは起こらないという前提のため、コンパイラはこれに関するコードの最適化は行いません。たとえば、x < x + 1が常に成り立つとは限りません。
比較演算子
比較演算子は、2つの値を比較して、bool型の値を返します。
== 等しい != 等しくない < 小なり <= 小なりイコール > 大なり >= 大なりイコール
オペランドは、比較可能である必要があります。つまり、最初のオペランドは、2番目のオペランドに代入可能であり、その逆も成り立たなくてはなりません。
演算子==と!=は、配列と構造体以外のすべての型に適用できます。これ以外の比較演算子に適用できるのは整数、浮動小数点、文字列だけです。比較結果は、下に示すよう定義されています。
- 整数の値は、一般的な方法で比較される。
- 浮動小数点の値は、IEEE-754の規定に従い比較される。
- 2つの複素数値
u、vは、real(u) == real(v)とimag(u) == imag(v)を両方満たせば同一である。 - 文字列の値は、バイト単位(辞書的)に比較される。
- 論理値は、ともに
trueであるか、ともにfalseであれば同一である。 - ポインタの値は、ともに同じ位置を示しているか、ともに
nilであれば同一である。 - 関数の値は、ともに同じ関数を参照しているか、ともに
nilであれば同一である。 - スライスの値は、
nilとだけ比較できる。 - チャネルとマップの値は、ともに同じ
make(§スライス、マップ、チャネルの作成) の呼び出しで作成されているか、ともにnilであれば同一である。 - インタフェースの値は、同一である動的な型と値を持つか、ともに
nilであれば同一である。 - インタフェースの値
xが、非インタフェースの値yと同一となるのは、xの動的な型が、yの静的な型と同一で、かつxの動的な値がyと同一であるとき。 - ポインタ、関数、スライス、チャネル、マップ、インタフェースの値は、明示的に
nilを代入されているか、または初期化されていないか、または他のnilである値から代入されていれば、nilと同一である。
論理演算子
論理演算子は論理値に適用され、オペランドと同じ型で結果を返します。右オペランドが評価されるかどうかは条件によります。
&& 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
この受信操作のときはブロックされません。受信操作が実行できたときは論理値型の変数ok にtrue が、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のとき、fはt.f(7)ではなく、 f(t, 7)として実行します。レシーバにバインドする関数を作成するには、クロージャを使ってください。
インタフェース型のメソッドから関数の値を取り出すこともできます。結果得られる関数は、そのインタフェース型のレシーバを必ず受け取ります。
変換
変換とはTが型であり、xがT型へ変換可能な式であるとき、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に変換可能です。
-
xは、Tに代入可能である。 -
xの型とTが、同一の基礎型である。 -
xの型とTが、無名のポインタ型で、それらのポインタのベース型が同一の基礎型を持つ。 -
xの型とTが、ともに整数型か浮動小数点型である。 -
xの型とTが、ともに複素数型である。 -
xが整数、または[]byte型か[]int型であり、Tが文字列型である。 -
xが文字列で、Tが[]byteか[]intである。
数値型と文字列型の相互変換にのみ適用されるルールがあります。これらの変換では、xが表現している値が変更される可能性があり、実行時のコスト増につながります。これ以外の変換では、型を変更するだけで、xの表現する値を変えることはありません。
整数型間の変換
- 整数型間の変換では、値が符号付き整数のときは、暗黙的に無限大の精度に符号拡張されます。符号なしのときは、ゼロ拡張されます。そのあとで変換結果となる型に一致するように切り捨てられます。たとえば、
v := uint16(0x10F0)のときは、uint32(int8(v)) == 0xFFFFFFF0になります。変換の結果は常に有効な値となり、オーバフローは起こしません。 - 浮動小数点数を整数に変換するとき、小数部は捨てられます。(ゼロに近づくよう切り捨て/切り上げ)
- 整数、または浮動小数点の値を浮動小数点型に、もしくは複素数の値を他の複素数型に変換するときは、結果となる値はその変換先の型で規定されている精度に丸められます。たとえば、
float32型の変数xの値は、格納されるときIEEE-754の32ビットの数値以上の精度が使われます。しかしfloat32(x)が表すのは32ビットに丸められたxの値です。同様に、x + 0.1は32ビット以上の精度が使われますが、float32(x + 0.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" -
[]byte型([]uint8と同義)の値を文字列型へ変換することで、スライスの要素の連続したバイトデータを持つ文字列が得られます。スライスの値がnilであれば結果は空文字列になります。string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø" []int型の値を文字列型へ変換することで、各整数値を文字列に変換し結合した文字列が得られます。スライスの値がnilであれば結果は空文字列になります。string([]int{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔"- 文字列の値を
[]byte型(または[]uint8型)へ変換することで、文字列のバイトデータを要素として持つスライスが得られます。文字列が空であれば結果は[]byte(nil)です。[]byte("hellø") // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'} - 文字列の値を
[]int型へ変換することで、文字列の各Unicodeコードポイントを持つスライスが得られます。文字列が空であれば結果は[]int(nil)です。[]int(MyString("白鵬翔")) // []int{0x767d, 0x9d6c, 0x7fd4}
ポインタと整数間で変換を行う仕組みは言語上はありません。ただしunsafeパッケージでは、ある程度の制限はありますがこの機能を実装しています。
定数式
定数式は定数オペランドだけを含み、コンパイル時に評価される式です。
論理値型、数値型、文字列型定数がオペランドとして合法的に使えるときは常に、型を持たない論理値型、数値型、文字列型定数をそれぞれオペランドとして使用できます。シフト演算を除き、二項演算のオペランドが型を持たない整数定数と、同じく型を持たない浮動小数点定数であるとき、整数定数は型を持たない浮動小数点定数に変換されます(/と%がこれに相当します)。同様に、複素数型のオペランドを合法に使えるときは常に、型を持たない整数または浮動小数点定数がオペランドとして使用でき、整数または浮動小数点定数は虚数部分をゼロとする複素数型に変換されます。
結果がbool型となる比較演算子を除き、型を持たない定数に演算子を適用した結果は、同種(すなわち、論理値型、整数型、浮動小数点型、複素数型、文字列型定数いずれか)の型を持たない定数となります。
虚数リテラルは型を持たない複素数定数(実数部分は0)で、型を持たない整数と浮動小数点定数の二項演算子によって作成されます。この演算結果は型を持たない複素数定数です。複素数定数は常に、虚数リテラルもしくは虚数リテラルから作られた定数を含む定数式、または組み込み関数cmplxで作られます。
const Σ = 1 - 0.707i const Δ = Σ + 2.0e-4 - 1/1i const Φ = iota * 1i const iΓ = cmplx(0, Γ)
定数式は、常に正確に評価されます。そのため評価中の値と定数は、言語でサポートされている事前定義済みの型よりかなり大きな精度を必要とするかもしれません。よって以下は正しい宣言です。
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()、 <-c、 g()、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 .
空ステートメント
空ステートメントは何も行いません。
EmptyStmt = .
ラベル付きステートメント
ラベル付きステートメントは、goto、break、continueステートメントの宛先となります。
LabeledStmt = Label ":" Statement . Label = identifier .
Error: log.Crash("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=はひとつのトークンを構成します。代入操作において、左辺、右辺双方の式リストは、単一値となるひとつの式でなければなりません。
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を入れ替え
代入において、それぞれの値は代入先オペランドの型に代入可能でなければなりません。型を持たない定数をインタフェース型の変数へ代入するとき、その定数はbool、int、float、complex、string型いずれかの値へ変換されます。どの型になるかは値が論理値、整数、浮動小数点、複素数、文字列型定数のどれであるかに依存します。
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つの形式があります。式switchのcaseには、switch式の値と比較するための式を記述します。型switchのcaseには、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 float:
printFloat(i) // iはfloat
case func(int) float:
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_float := v.(float); is_float {
printFloat(i) // iはfloat
} else if i, is_func := v.(func(int) float); 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 = ExpressionList ( "=" | ":=" ) "range" Expression .
range節の右側の式の型は、配列、スライス、文字列、マップ、配列へのポインタ、チャネルのどれかでなければなりません。チャネルのときを除いて、左辺の識別子リスト(ExpressionList)は、1~2個の式(代入と同じく、これらは変数、ポインタ間接参照、フィールドセレクタ、インデックス式のどれか)でなければなりません。各繰り返しにおいて、1番目のイテレーション変数にセットされるのは、文字列または配列またはスライスのインデックス、マップのキーのいずれかであり、2番目のイテレーション変数があれば、それにセットされるのは1番目の変数と対応する文字列、配列要素、マップの値です。配列またはスライスのインデックスの型(これは常にint)、要素の型、マップのキーおよび値の型は、対応するイテレーション変数に対して代入可能でなければなりません。右側の式はループを開始する前に一度だけ評価されます。ループの各回でrange節から取り出された値が、代入ステートメントと同じく左側に代入されます。左側で関数呼び出しを行うと繰り返しの度に評価されます。
文字列型の値を扱うとき、range節は文字列内のUnicodeコードポイントを繰り返します。連続する繰り返しの際、インデックス用変数にセットされる値は、文字列中の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 .
この式は関数またはメソッドの呼び出しでなければなりませんが、通常の呼び出しとは異なり、プログラムは実行された関数の完了を待ちません。
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" ( 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通りの方法があります。
- 戻り値として単一値または複数値が
returnステートメントで明示的にリストされます。個々の式は単一値であり、かつ対応する関数の戻り値の型に代入可能でなければなりません。func simple_f() int { return 2 } func complex_f1() (re float, im float) { return -7.0, -4.0 } returnステートメントの式リストは、複数値を返す関数の(一回の)呼び出しです。これは関数から返されたそれぞれの値が、適切な型を持ったテンポラリの変数に代入され、その変数がreturnステートメントにリストされるかのように振る舞います。そのあとは前のケースで説明した規則が当てはまります。func complex_f2() (re float, im float) { return complex_f1() }- 関数の戻り値のパラメータに名前をつけているときは、
returnの式を空にすることができます(§関数型)。戻り値パラメータは、通常のローカル変数と同じで、関数内で必要に応じて値の代入ができます。returnステートメントは、これら変数に格納されている値を返します。func complex_f3() (re float, im float) { re = 7.0 im = 4.0 return }
どう宣言されるかに関係なく、すべての戻り値は、関数が呼び出された直後に、それぞれの型のゼロ値で初期化されます。(§ゼロ値)
breakステートメント
breakステートメントは、最も内側にあるfor、switch、selectステートメントの実行を終了します。
BreakStmt = "break" [ Label ] .
ラベルが指定されているときは、そのラベルはfor、switch、selectステートメントのどれかを伴っていなければならず、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の作成をスキップしてしまうため誤りです。
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、closed
cをチャネルと仮定したとき、事前宣言済み関数close(c)は、これ以上の値を送信操作により受け付けないようチャネルをマーキングします。closeより前に送信された値を受信したあとは、受信操作はそのチャネル型のゼロ値を返すようになります。このようにゼロ値が受信されたあとは、closed(c)はtrueを返すようになります。
長さ、キャパシティ
組み込み関数lenとcapは様々な型の引数を取り、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ではない)を返します。このときメモリの内容は初期値のセクション(§ゼロ値)で記述されているとおりに初期化されます。
呼び出し 型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型の非同期チャネル
引数nとmは、整数型でなければなりません。nがマイナス値であるかmより大きいとき、もしくはnまたはmが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要素を初期容量として持つマップ
スライスのコピー
組み込み関数copyは、コピー元srcからコピー先dstへスライスの要素をコピーします。コピー元とコピー先の重複は許されます。両引数の要素型が同じT型で、[]T型のスライスに代入可能である必要があります。コピーされる要素数はlen(src)とlen(dst)の短い方が使われます。
copy(dst, src []T) int
例:
var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
var s = make([]int, 6)
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}
複素数の作成、分解
複素数の作成と分解を行う3つの関数が用意されています。組み込み関数のcmplxは、浮動小数の実数と虚数部分から複素数の値を作成します。realとimagは複素数の値から、それぞれ実数と虚数部分を取り出します。
cmplx(realPart, imaginaryPart floatT) complexT real(complexT) floatT imag(complexT) floatT
引数の型と戻り値の型は対応しています。cmplxの場合は、2つの引数は同一の浮動小数の型であり、その戻り値の型は引数の浮動小数の型と対応する複素数型です。対応の組みわせは、complexとfloat、complex64とfloat32、complex128とfloat64です。realとimag関数にもこれと逆があてはまります。ゆえに、「z, z == cmplx(real(z), imag(z))」となります。
これらの関数のオペランドがすべて定数であるとき、戻り値も定数となります。
var a = cmplx(2, -2) // has type complex var b = cmplx(1.0, -1.4) // has type complex x := float32(math.Cos(math.Pi/2)) var c64 = cmplx(5, -x) // has type complex64 var im = imag(b) // has type float var rl = real(c64) // type float32
パニックの制御
組み込み関数のpanicとrecoverは、ランタイムパニックおよびプログラム定義のエラーにおける、状態の報告と制御を手助けします。
func panic(interface{})
func recover() interface{}
関数Fの中でpanicを呼び出すとFの実行はすぐに停止します。F関数内でdeferにより遅延指定された関数があれば実行された後、Fは呼び出し元に復帰します。Fの呼び出し元側では、F関数を呼び出した箇所があたかもpanic関数を呼び出したかのように振舞い、実行を停止し遅延指定した関数を実行します。この連鎖はゴルーチン内の全ての関数が実行を停止するまで、呼び出しとは逆順に続けられます。最後にプログラムが終了し、panicに渡された引数の値とともにエラーの状態が報告されます。この終了シーケンスは「パニックする」「パニクる」と呼ばれます。
recover関数を使うとプログラム側でパニック中のゴルーチンを制御できます。遅延起動した関数内でrecoverを呼び出す(それ以外の関数は呼び出さない)と停止した実行を再開することでパニックシーケンスを停止し、panicの呼び出しの際に渡されたエラー情報が返されます。遅延起動された関数以外でrecoverを呼び出しても、パニックシーケンスは中断することはありません。ゴルーチンがパニック中でないときrecoverはnilを返します。
func f(hideErrors bool) {
defer func() {
if x := recover(); x != nil {
println("panicking with value", x)
if !hideErrors {
panic(x) // go back to panicking
}
}
println("function returns normally") // executes only when hideErrors==true
}()
println("before")
p()
println("after") // never executes
}
func p() {
panic(3)
}
このf関数をhideErrors=trueで呼び出すと、次のように出力されます。
before panicking with value 3 function returns normally
fを呼び出した関数では処理が継続されます。hideErrors=falseのときは次のように出力されます。
before panicking with value 3
さらにどこかでrecoverが呼ばれなければ、このプログラムは終了します。
遅延指定された関数は、その関数を呼び出した側が戻り値を返す前に実行されるので、関数リテラルを使っていればパニック中に戻り値の値を変更できます。これは関数が自分の内部で発生したパニックから、呼び出し元を保護することを可能にします。
func IsPrintable(s string) (ok bool) {
ok = true
defer func() {
if recover() != nil {
println("input is not printable")
ok = false
}
// Panicking has stopped; execution will resume normally in caller.
// The return value will be true normally, false if a panic occurred.
}
panicIfNotPrintable(s) // will panic if validations fails.
}
ブートストラッピング
現在の実装では、ブートストラッピング(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の解釈は実装に依存しますが、一般的にはコンパイル済みパッケージのパス名の一部であり、パッケージのリポジトリへの相対パスです。
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の影響を受けているときに、AはBに依存しているとみなされます。ある2つのアイテムがお互いに依存していなければ、それらはソース内に出現した順で初期化されます。依存関係の分析はパッケージごとに行われるため、Aのイニシャライザが、Bを参照している別のパッケージで定義されている関数を呼び出したときの結果は規定されていません。
初期化コードが、“go”ステートメントを含んでいたとしても、全プログラムの初期化が終了するまで、そのステートメントで指定した関数の実行を開始しません。したがって、すべての初期化コードは単一のゴルーチン内で動作します。
init()関数はプログラムのどこからも参照することができません。すなわち、明示的に呼び出すことも、変数にこの関数のポインタを代入することもできません。
パッケージがインポートを伴うときは、このパッケージ自身の初期化より前に、インポートされたパッケージが初期化されます。ひとつのパッケージを複数回インポートしても、そのパッケージが初期化されるのは一回だけです。
パッケージのインポートでは構造上、初期化の依存関係が循環しないことが保証されています。
完成されたプログラムは、次の関数を持つmainパッケージを持たなくてはなりません。
func main() { ... }
これは複数のパッケージを結合して作られたとしても同様です。このmain.main()関数は、引数および戻り値を持ちません。
プログラムの実行はmainパッケージを初期化したあと、main.main()関数を実行することで開始されます。
main.main()から抜けるとプログラムは終了します。このとき、他(main以外)のゴルーチンの終了待ちは行いません。
実装上の制約:コンパイラはmainパッケージが他のどのパッケージからもインポートされることはないと仮定しています。
ランタイムパニック
配列に範囲外のインデックスを指定したときに発生するような実行エラーは、ランタイムパニックの引き金となります。これは組み込み関数panicをインタフェース型のruntime.Errorの値と共に呼び出したことと同じです。そのインタフェース型にはString() stringメソッドが少なくとも定められますが、今のところ個々のランタイムエラーの状態を正確に表す値についての仕様は決まっていません。
package runtime
type Error interface {
String() string
// and perhaps others
}
システム考察
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
Alignof、Offsetof、Sizeofの呼び出しは、コンパイル時にint型の定数となります。
unsafe.Typeof、unsafe.Reflect、unsafe.Unreflect関数は、実行時にインタフェースに格納されている動的な型および値へのアクセスを許します。Typeofは、valの動的な型を runtime.Typeとして返します。Reflectは、valの動的な値のコピーを割り当て、型とそのコピーのアドレスを返します。UnreflectはReflectの逆で、型とアドレスからインタフェースの値を作成します。これらの関数を利用しているreflectパッケージでは、インタフェースの値を安全、かつより便利に調べる方法を提供しています。
サイズとアライメントの保証
数値型(§数値型)では、以下に示すサイズが保証されています。
型 サイズ(バイト数) byte, uint8, int8 1 uint16, int16 2 uint32, int32, float32 4 uint64, int64, float64 8
以下に示す、アライメントの特性が最低限保証されています。
- 変数
x(型は問わない):1 <= unsafe.Alignof(x) <= unsafe.Maxalign - 変数
xが数値型のとき:unsafe.Alignof(x)は、unsafe.Sizeof(x)およびunsafe.Maxalign以下であり、少なくとも1以上である。 - 変数
xが構造体型のとき:unsafe.Alignof(x)は、xの各フィールドをfとしたときunsafe.Alignof(x.f)の中で一番大きい値と同じであり、少なくとも1以上である。 - 変数
xが配列型のとき:unsafe.Alignof(x)は、unsafe.Alignof(x[0])と同じであり、少なくとも1以上である。
実装との差異 – TODO
- 実装は、gotoステートメント、および(宣言以外の)宛先に対する制約を守っていません。
- メソッド式は未実装です。
- Gccgo:ひとつのソースファイルに、ひとつのinit()関数しか許していません。
- Gccgo:deferにより遅延実行指定された関数から、deferを呼び出した関数の戻り値パラメータにはアクセスできません。
- Gccgo:関数の戻り値はアドレス指定可能ではありません。
- Gccgo:recoverは実装されていません。
- Gccgo:panicの実装が仕様と異なります。
Trackback URL
Comments