本文書は,The Go Programming Language Specification version 2021/02/10 のなんちゃって日本語訳である.
- 原文ソース:https://github.com/golang/go/blob/master/doc/go_spec.html
- 訳文ソース:https://github.com/hiwane/gospec-ja.誤訳・誤字脱字などは issue かプルリクで
- https://hiwane.github.io/gospec-ja/
訳注
- valid/invalid は有効/無効, legal/illegal は正当/不当と訳す.
- letter と character を区別するため,letter は英字,character は文字と訳す.
- signed/unsigned 符号付き,符号なし
- source code と source text
- parameter=パラメーター,argument=引数
- アドレス可能 (addressable)
- slice indexing operation (address operator)
- map index expression (assignments)
- ポインター間接参照 (pointer indirection)
- else branch... else節, else枝... case節 case枝 section 節
- defined type / type definition
- 符号位置 code point
- @@@ はこなれていない.どうすんべ
- now: 2 周目: リンクと強調: 表現可能性
訳注:記号
記号 | 日本語(本文) | 英語(原文) |
---|---|---|
_ |
アンダースコア | underscore character |
' |
シングルクォート | single quote |
" |
ダブルクォート | double quote |
` |
バッククォート | back quote |
\ |
バックスラッシュ | backslash |
() |
丸括弧 | parentheses |
{} |
波括弧 | brace brackets |
[] |
角括弧 | square brackets |
- イントロダクション
- 表記
- ソースコード表現
- 字句要素
- 定数
- 変数
- 型
- インターフェース型
- 型と値の性質
- ブロック
- 宣言とスコープ
- 式
- 文
- ビルトイン関数
- パッケージ
- プログラムの初期化と実行
- エラー
- ランタイムパニック
- システムの考慮事項
本文書は Go プログラミング言語のリファレンスマニュアルである. 他の情報源としては,golang.org を参照されたい.
Go はシステムプログラミングを念頭において設計された,汎用言語である. 強い型付け,ガベージコレクションを持ち,並行プログラミング (concurrent programming) を明示的にサポートしている. プログラムは, 性質が依存関係の効率的な管理を実現するパッケージ (package) で構成される.
文法はコンパクト,かつ,解析が単純であり, 統合開発環境などの自動ツールによる簡易な解析が可能になる.
構文は,Extended Backus-Naur Form(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 "}" .
Production
は, Term
と以下の演算子によって構成される式 (expression) である.
以下の演算子は優先順位の昇順に記載している.
| 代替 (alternation)
() グループ化 (grouping)
[] オプション (0 回か 1 回)
{} 繰り返し (0 回から n 回)
小文字の Production
名 (production_name
) は,字句トークンを識別するために使用する.
非終端はキャメルケース (訳注: CamelCase
のように複合語を一綴りとして,要素語の最初を大文字にする) である.
字句トークンはダブルクォーテーション ""
またはバッククォート ``
で囲む.
形式 a … b
は,a
から b
の文字の集合の代替に使用する.
三点リーダー (horizontal ellipsis) …
は,
本仕様の他の場所でも,ここで指定していない列挙やコードスニペットを非公式に表すために使用する.
文字 …
(3文字 ...
とは対照的に) は Go言語のトークンではない.
ソースコードは UTF-8 でエンコードされた Unicode テキストである. テキストは正規化(canonicalized)されていない. そのため,単一のアクセント付き符号位置は, アクセントと英字を組み合わせて構築される同じ文字と区別される. これらは,2 つの符号位置として扱われる. 単純にするために, 本文書では, 修飾されてない用語の文字 (character) をソーステキスト内の Unicode 符号位置を参照するために使用する.
各符号位置 (code point) は区別され,例えば,大文字と小文字の英字は異なる文字として扱われる.
実装上の制限:
他のツールとの互換性のために,
コンパイラはソースコード上で NUL 文字 (U 0000
) を許可しない場合がある.
実装上の制限:
他のツールとの互換性のために,
コンパイラは,UTF-8でエンコードされたバイトオーダーマーク (BOM) (U FEFF
)
がソースコード上の最初の Unicode 符号位置であるときに無視できる.
バイトオーダーマークは,ソース内の他の場所では許可されない場合がある.
以下の項 (term) は, 特定の Unicode 文字クラス (character class) を示すために使用する.
newline = /* Unicode 符号位置 (code point) U 000A */ .
unicode_char = /* 任意の newline を除いた Unicode 符号位置 */ .
unicode_letter = /* "文字 / Letter" に分類される符号位置 */ .
unicode_digit = /* "数, 10進数 / Number, decimal digit" に分類される符号位置 */ .
The Unicode Standard 8.0 では,
4.5節 "General Category" 節は文字カテゴリの集合を定義する.
Go は,英字カテゴリの Lu
, Ll
, Lt
, Lm
, Lo
に属するすべての文字を Unicode 英字として扱い,
数字カテゴリ Nd
に属する文字を Unicode 数字として扱う.
訳注:
value | Category Major, minor | カテゴリー |
---|---|---|
Lu | Letter, uppercase | 文字,大文字 A,B,C,... |
Ll | Letter, lowercase | 文字,小文字 a,b,c,... |
Lt | Letter, titlecase | 文字,タイトル文字 |
Lm | Letter, modifier | 文字,修飾 |
Lo | Letter, other | 文字,その他 |
Nd | Number, decimal digit | 数字,10進数字 0,1,... |
アンダースコア文字 _
(U 005F
) は,
英字 (letter) として考える.
letter = unicode_letter | "_" .
decimal_digit = "0" … "9" .
binary_digit = "0" | "1" .
octal_digit = "0" … "7" .
hex_digit = "0" … "9" | "A" … "F" | "a" … "f" .
コメントはプログラムのドキュメントとして機能する. 次の 2 つの形式がある.
- 行コメント (line comment): 文字列
//
から,行末まで - 一般的なコメント (general comment):
/*
で始まり,最初の後続の*/
まで.(訳注:入れ子不可/* ABC /* DEF */ GHI */
ではGHI
はコメントではない)
コメントは,ルーンや文字列リテラル内,または,コメント内では開始できない. 改行を含まない「一般的なコメント」はスペースのように機能する. 他のコメントは改行のように機能する.
トークンは Go言語の語彙を形成する.
識別子 (identifier),
キーワード (keyword),
演算子 (operator) と区切り (punctuation),
リテラル (literal) の 4 つのクラスがある.
空白 (U 0020
),
水平タブ (U 0009
),
キャリッジ リターン (CR, U 000D
),
改行文字 (ラインフィード,LF,U 000A
) から形成される
ホワイトスペースは,
単一に結合するであろうトークンを分離する場合を除いて無視される.
また,改行文字とファイルの末尾はセミコロンを挿入するトリガーになる場合がある.
入力をトークンに分割する間,
次のトークンは有効なトークンを形成する最長の文字シーケンスである.
正式な文法では,セミコロン ;
を多くの Production
の終端として使用する.
Goプログラムでは,次の 2 つの規則を利用して,多くの場合セミコロンを省略できる.
- 入力をトークンに分割するとき, 行の最後のトークンが以下の場合,その後ろにセミコロンが自動的に挿入される.
- 識別子
- 整数リテラル,浮動小数点リテラル, 虚数リテラル,ルーンリテラル,文字列リテラル
- キーワード
break
,continue
,fallthrough
,return
- 演算子や区切り
--
,)
,]
,}
- 複雑な文が 1行を専有できるようにするために,セミコロンは閉じカッコ
)
,}
の前では省略できる.
慣用的な使用を反映するために, 本ドキュメントのコード例では,これらの規則によりセミコロンを省略する.
識別子 (identify) は,変数 (variable) や型 (type) などのプログラムエンティティ (entity) に名付ける. 識別子は,1 つ以上の英字 (letter) と数字 (digit) の列である. 識別子は,英字から始まらなければならない.
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
以下の文字列は演算子 (代入演算子 (assignment operators) を含む) と区切り (punctuation) である.
& = &= && == != ( )
- | -= |= || < <= [ ]
* ^ *= ^= <- > >= { }
/ << /= <<= = := , ;
% >> %= >>= -- ! ... . :
&^ &^=
整数リテラル (integer literal) は,
整数定数を表現する数字の列である.
オプションの接頭辞は,
非10進数を表現する.
0b
と 0B
は2進数,
0o
と 0O
は8進数,
0x
と 0X
は16進数を表現する.
単独の 0
は10進数のゼロとみなされる.
16進数では,英字 a-f
と A-F
がそれぞれ 10-15
の値を表す.
読みやすさのため,
アンダースコア _
が接頭辞の後ろ,または,続く数字との間に使用される場合がある.
このアンダースコアは,リテラルの値を変更しない.
int_lit = decimal_lit | binary_lit | octal_lit | hex_lit .
decimal_lit = "0" | ( "1" … "9" ) [ [ "_" ] decimal_digits ] .
binary_lit = "0" ( "b" | "B" ) [ "_" ] binary_digits .
octal_lit = "0" [ "o" | "O" ] [ "_" ] octal_digits .
hex_lit = "0" ( "x" | "X" ) [ "_" ] hex_digits .
decimal_digits = decimal_digit { [ "_" ] decimal_digit } .
binary_digits = binary_digit { [ "_" ] binary_digit } .
octal_digits = octal_digit { [ "_" ] octal_digit } .
hex_digits = hex_digit { [ "_" ] hex_digit } .
42
4_2
0600
0_600
0o600
0O600 // 2文字目は大文字 `O` である
0xBadFace
0xBad_Face
0x_67_7a_2f_cc_40_c6
170141183460469231731687303715884105727
170_141183_460469_231731_687303_715884_105727
_42 // 整数リテラルではなく,識別子
42_ // 無効: _ は連続する数字を区切る必要がある
4__2 // 無効: _ は一度にひとつのみ
0_xBadFace // 無効: _ は連続する数字を区切る必要がある
浮動小数点リテラルは,浮動小数点定数の10進数または16進数表現である.
10進浮動小数点リテラルは,整数部 (10進数),小数点,小数部 (10進数),
指数部 (e
または E
とオプションの符号,10進数)から成る.
整数部と小数部のどちらか一方は省略でき,
小数点と指数部のどちらか一方は省略できる.
指数値 exp
は仮数 (整数部と小数部)を 10^{exp}
倍する.
16進数浮動小数点リテラルは,接頭 0x
または 0X
,
整数部 (16進数),
基数点 (radix point; 訳注 n進数小数点のこと),
小数部 (16進数),
指数部 (p
または P
とオプションの符号,10進数)から成る.
整数部と小数部のどちらか一方は省略できる.
基数点は省略できるが,指数部は必要である.
(この構文は IEEE 754-2008 §5.12.3. で与えられる構文と一致)
指数値 exp
は仮数 (整数部と小数部)を 2^{exp}
倍する.
読みやすさのため,
アンダースコア _
が接頭辞の後ろ,または,続く数字との間に使用される場合がある.
このアンダースコアは,リテラルの値を変更しない.
float_lit = decimal_float_lit | hex_float_lit .
decimal_float_lit = decimal_digits "." [ decimal_digits ] [ decimal_exponent ] |
decimal_digits decimal_exponent |
"." decimal_digits [ decimal_exponent ] .
decimal_exponent = ( "e" | "E" ) [ " " | "-" ] decimal_digits .
hex_float_lit = "0" ( "x" | "X" ) hex_mantissa hex_exponent .
hex_mantissa = [ "_" ] hex_digits "." [ hex_digits ] |
[ "_" ] hex_digits |
"." hex_digits .
hex_exponent = ( "p" | "P" ) [ " " | "-" ] decimal_digits .
0.
72.40
072.40 // == 72.40
2.71828
1.e 0
6.67428e-11
1E6
.25
.12345E 5
1_5. // == 15.0
0.15e 0_2 // == 15.0
0x1p-2 // == 0.25
0x2.p10 // == 2048.0
0x1.Fp 0 // == 1.9375
0X.8p-0 // == 0.5
0X_1FFFP-16 // == 0.1249847412109375
0x15e-2 // == 0x15e - 2 (integer subtraction)
0x.p1 // 無効: 仮数部に数字がない
1p-2 // 無効: p 指数には 16進数仮数部が必要
0x1.5e-2 // 無効: 16進化数部は p 指数が必要
1_.5 // 無効: _ は連続する数字を区切る必要がある
1._5 // 無効: _ は連続する数字を区切る必要がある
1.5_e1 // 無効: _ は連続する数字を区切る必要がある
1.5e_1 // 無効: _ は連続する数字を区切る必要がある
1.5e1_ // 無効: _ は連続する数字を区切る必要がある
虚数リテラル (imaginary literal) は複素数定数の虚数部を表す.
虚数リテラルは,整数リテラルまたは浮動小数点リテラルと,その後ろに続く小文字英字 i
から成る.
虚数リテラルの値は,それぞれ,整数リテラルまたは浮動小数点リテラルに虚数単位 i
を乗じた値である.
imaginary_lit = (decimal_digits | int_lit | float_lit) "i" .
後方互換のため,
虚数リテラルの整数部 (および場合によってはアンダースコア) が
10進数のみで構成される場合は,
0
で始まっていたとしても 10進数と見なされる.
0i
0123i // == 123i (後方互換のため)
0o123i // == 0o123 * 1i == 83i
0xabci // == 0xabc * 1i == 2748i
0.i
2.71828i
1.e 0i
6.67428e-11i
1E6i
.25i
.12345E 5i
0x1p-2i // == 0x1p-2 * 1i == 0.25i
ルーンリテラル (rune literal) は
Unicode 符号位置を特定する整数値である
ルーン定数を表現する.
ルーンリテラルは,'x'
や '\n'
のようにシングルクォートで囲まれた 1つ以上の文字たちで表現される.
シングルクォート内では,
改行とエスケープされていないシングルクォートを除く任意の文字を使用できる.
シングルクォートで囲まれた1文字は,その文字自身の Unicode 値を表すが,
バックスラッシュで始まる複数の文字たちの場合は,
様々な形式で値をエンコードする.
最もシンプルな形式は,シングルクォートで囲まれた単一の文字を表現する.
Go のソーステキストは UTF-8 でエンコードされた Unicode 文字たちなので,
複数の UTF-8 エンコードバイトは,単一の整数値を表現する場合がある.
例えば,リテラル 'a'
はリテラル a
, Unicode U 0061
,値 0x61
を保持し,
'ä'
は 2バイト (0xc3 0xa4
) は
リテラル a-ウムラウト
, U 00E4
, 値 0xe4
を保持する.
いくつかのバックスラッシュエスケープたちにより,
任意の値を
ASCII テキストとしてエンコードできる.
数定数として整数は 4 つの方法で表現できる:
\x
と正確に 2 桁の 16進数;
\u
と正確に 4 桁の 16進数;
\U
と正確に 8 桁の 16進数;
素のバックスラッシュ \
と正確に 3 桁の 8 進数.
いずれの場合も,リテラルの値は,対応する基数の数字で表される値である.
これらの表現はすべて整数を表すが,
異なる有効範囲をもつ.
8進エスケープは 0 から 255 までの値を表さなければならない.
16進エスケープは,構成からこの条件を満足する.
'\u'
と '\U'
のエスケープは
Unicode 符号位置を表現するので,
その中には不当な値,
特に 0x10FFFF
より大きな値とサロゲートハーフたちが含まれる.
バックスラッシュの後,特定の一文字のエスケープは特別な値を表す:
\a U 0007 アラート または ベル
\b U 0008 バックスペース
\f U 000C form feed
\n U 000A ラインフィード (line feed) または ニューライン (newline)
\r U 000D キャリッジリターン (carriage return)
\t U 0009 水平タブ
\v U 000b 垂直タブ
\\ U 005c バックスラッシュ
\' U 0027 シングルクォート (ルーンリテラル内でのみ有効なエスケープ)
\" U 0022 ダブルクォート (文字列リテラル内でのみ有効なエスケープ)
バックスラッシュで始まる他のすべての列は,ルーンリテラルの中では不当である.
rune_lit = "'" ( unicode_value | byte_value ) "'" .
unicode_value = unicode_char | little_u_value | big_u_value | escaped_char .
byte_value = octal_byte_value | hex_byte_value .
octal_byte_value = `\` octal_digit octal_digit octal_digit .
hex_byte_value = `\` "x" hex_digit hex_digit .
little_u_value = `\` "u" hex_digit hex_digit hex_digit hex_digit .
big_u_value = `\` "U" hex_digit hex_digit hex_digit hex_digit
hex_digit hex_digit hex_digit hex_digit .
escaped_char = `\` ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | `\` | "'" | `"` ) .
'a'
'ä'
'本'
'\t'
'\000'
'\007'
'\377'
'\x07'
'\xff'
'\u12e4'
'\U00101234'
'\'' // シングルクォート文字を含むルーンリテラル
'aa' // 不当: 文字が多すぎる
'\xa' // 不当: 16進数桁数が少ない
'\0' // 不当: 8進数の桁数が少ない
'\uDFFF' // 不当: サロゲートハーフ
'\U00110000' // 不当: 無効な Unicode 符号位置
文字列リテラル (string literal) は, 文字の列を連結して得られる文字列定数を表現する. 生の文字列リテラル (raw string literal) と 解釈された文字列リテラル (interpreted string literal) の 2 つの形式がある.
生の文字列リテラルは
`foo`
のようにバッククォートで囲まれた文字列である.
バッククォート内では,
バッククォートを除く任意の文字を使用できる.
生の文字列リテラルの値は,
バッククォート間の解釈されない(暗黙的に UTF-8 でエンコードされた)
文字で構成される文字列である;
特に,
バックスラッシュには特別な意味はなく,
文字列は改行を含まれる場合がある.
生の文字列リテラル内のキャリッジリターン ('\r'
) は
生の文字列値から破棄される.
解釈される文字列リテラルは,
"bar"
のようにダブルクォートで囲まれた文字列である.
ダブルクォート内では,
改行とエスケープされていないダブルクォートを除く任意の文字を使用できる.
ダブルクォート間のテキストは
リテラルの値を形成し,
バックスラッシュエスケープは
ルーンリテラルたち (\'
は不当,\"
は正当という点を除く)
であると解釈され, 同様の制限がある.
3桁の 8進数 (\nnn
) エスケープと 2桁の 16進数 (\xnn
) エスケープは,
結果の文字列の個々のバイトたちを表す.
すべての他のエスケープは
個々の文字たちの
(マルチバイト)UTF-8 エンコードを表す.
したがって,
文字列リテラル内の \377
と \xFF
は
単一バイトの値 0xFF = 255
を表し,
ÿ
, \u00FF
, \U000000FF
, \xc3\xbf
は
文字 U 00FF
の UTF-8 エンコードの 2 バイト 0xc3 0xbf
を表す.
string_lit = raw_string_lit | interpreted_string_lit .
raw_string_lit = "`" { unicode_char | newline } "`" .
interpreted_string_lit = `"` { unicode_value | byte_value } `"` .
`abc` // "abc" と同じ
`\n
\n` // "\\n\n\\n" と同じ
"\n"
"\"" // `"` と同じ
"Hello, world!\n"
"日本語"
"\u65e5本\U00008a9e"
"\xff\u00FF"
"\uD800" // 不当: サロゲートハーフ
"\U00110000" // 不当: 無効な Unicode 符号位置
以下の例は,すべて同じ文字列の表現である.
"日本語" // UTF-8 入力テキスト
`日本語` // 生のリテラルとしての UTF-8 入力テキスト
"\u65e5\u672c\u8a9e" // 明示的な Unicode 符号位置
"\U000065e5\U0000672c\U00008a9e" // 明示的な Unicode 符号位置
"\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e" // 明示的な Unicode バイトたち
ソースコードが アクセントと文字を含む結合形式など, 2 つの符号位置として文字を表す場合, ルーンリテラル(単一の符号位置ではない)に配置すると, 結果はエラーとなり, 文字列リテラルに配置すると, 2 つの符号位置として使用される.
ブール定数,ルーン定数,整数定数,浮動小数点整数, 複素数定数,文字列定数がある. ルーン定数,整数定数,浮動小数点定数,複素数定数を総称して,数値定数と呼ばれる.
定数値は,ルーンリテラル,
整数リテラル,
浮動小数点リテラル,
虚数リテラル,
文字列リテラル,
定数を表す識別子,
定数式,
結果が定数となる変換,
任意の値に適用される unsafe.Sizeof
,
ある式に適用される cap
, len
,
複素数定数に適用される real
, imag
,
数値定数に適用される complex
のようなビルトイン関数の復帰値
によって表現される.
ブール真偽値は
事前宣言された定数 true
と false
によって表現される.
事前宣言された識別子 iota
は整数定数を示す.
一般に,複素数定数は定数式の形式であり,その節で説明する.
数値定数は任意精度の正確な値を表現し,
オーバーフローしない.
したがって,IEEE-754 の負のゼロ,無限大,非数値 (NaN
) を示す定数はない.
定数は,型付きでも,型なしでもいい.
リテラル定数,true
, false
, iota
, 型なし定数オペランドを含む定数式は,
型なしである.
定数は, 定数宣言や定数変換によって 明示的に型を指定されるか, もしくは, 変数宣言や代入で使用するか, 式のオペランドとして暗黙的に指定される. 定数値がそれぞれの型の値として表現されない場合には, エラーになる.
型なし定数は,
デフォルトの型を持つ.
デフォルトの型は,
例えば,明示的な型がない i := 0
のような簡潔な変数宣言では,
型付き変数が要求する
コンテキストに暗黙的に変換される.
型なし定数のデフォルトの型は,
真偽値定数,ルーン定数,整数定数,浮動小数点定数,複素数定数,文字列定数はそれぞれ
bool
, rune
, int
, float64
, complex128
, string
である.
実装上の制限: 数値定数は言語では任意精度を持つが, コンパイラは精度が制限された内部表現を使用して実装する場合がある. ただし,すべての実装は以下を満たす (MUST):
- 整数定数は少なくとも 256bit を表現する
- 少なくとも 256bit の仮数部と少なくとも 16bit の符号付き 2 進指数を使用して, 複素数定数の部分を含む,浮動小数点定数を表現する
- 整数定数が正確に表現できない場合にはエラーを返す
- 浮動小数点定数または複素数定数がオーバーフローのために表現できない場合にはエラーを返す
- 浮動小数点定数または複素数定数が精度の制限によって表現できない場合には,最も近い表現可能な定数に丸める
これらの要求は, リテラル定数と定数式の評価結果の両方に適用される.
変数 (variable) は,値を保持するための保管場所である. 許容値の集合は,変数の型によって決まる.
変数宣言,または,
関数の引数と復帰値のための関数宣言や関数リテラルのシグネチャーは,
名前付き変数の保管場所を予約する.
ビルトイン関数 new
の呼び出しや,
複合リテラル (composite literal) のアドレスの取得は,
実行時に変数の保管場所が割り当てられる.
そのような無名変数 (anonymous variable) は,
(暗黙の可能性もある) ポインター間接 (pointer indirection)
参照される.
配列 (array),スライス (slice), 構造体型の 構造化変数 (structured variable) は,個別にアドレス指定できる要素とフィールドがある. そのような各要素は変数のように機能する.
変数の静的な型 (static type) (または単に型)は,
その宣言で指定された型,
new
呼び出しや複合リテラルによって与えられる型,
構造化変数の要素の型である.
インターフェース型の変数もまた,
実行時に代入された値の具体的な型 (型を持たない事前宣言された識別子 nil
の場合を除く) である
個別の動的な型 (dynamic type) がある.
動的な型は,
実行中に変化する可能性があるが,
インターフェース変数に割り当てられた値は
常に変数の静的な型に代入可能 (assignable) である.
var x interface{} // x は nil であり,静的な型 interface{} をもつ
var v *T // v は nil であり,静的な型 *T をもつ
x = 42 // x は 値 42 を持ち,動的な型 int をもつ
x = v // x 値 (*T)(nil) であり,動的な方 *T をもつ
変数の値は, 式に含まれる変数を参照することによって取得される; それは,変数に代入された最新の値である. 変数に値が割り当てられていない場合, その値は,その型のゼロ値である.
型 (type) は, オペレーションの値と, これらの値に指定されるメソッドの集合を決定する. 型は, 型名がある場合は型名 (type name) を示すか, 既存の型から構成される型リテラル (type literal) を使って 指定できる.
Type = TypeName | TypeLit | "(" Type ")" .
TypeName = identifier | QualifiedIdent .
TypeLit = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
SliceType | MapType | ChannelType .
Go言語は,特定の型名を事前宣言 (predeclare)する. その他は,型宣言で導入される. 複合型 (配列型,構造体型,ポインター型,関数型,インターフェース型,スライス型,マップ型,チャンネル型) は型リテラルを使って構築できる.
各型 T
は基底型 (underlying type) をもつ.
T
が事前宣言されたブール型,数値型,文字列型の一つであるか,
型リテラルである場合,
対応する基底型は T
自身となる.
それ以外の場合,
T
の基底型は
T
が型宣言で参照する型の基底型である.
type (
A1 = string
A2 = A1
)
type (
B1 string
B2 B1
B3 []B1
B4 B3
)
A1
, A2
, B1
, B2
の基底型は文字列型である.
[]B1
, B3
, B4
の基底型は []B1
である.
訳注:本家のままだが,[]B1
は必要?
型は,
型に関連付けられたメソッド集合 (method set)がある場合がある.
インターフェース型 (interface type) のメソッド集合は,そのインターフェース自身である.
それ以外の型 T
のメソッド集合は,
レシーバー型 (receiver type) T
で宣言されたすべてのメソッドで構成される.
ポインター型 *T
に対応するメソッド集合は,
レシーバー *T
もしくは T
で宣言されたすべてのメソッド集合である.
(つまり,それは T
のメソッド集合もまた,含む.)
構造体型の章で説明しているように,
埋め込みフィールドを含む構造体にさらなる規則が適用される.
それ以外の型は,空のメソッド集合をもつ.
メソッド集合において,
それぞれのメソッドは一意で,
ブランクでないメソッド名を持たなければならない (MUST).
ある型のメソッド集合は, その型が実装するインターフェースと, その型のレシーバーを用いて呼び出すことができるメソッドによって決定する.
ブール型 (boolean type) は,
事前宣言された定数 true
と false
で表記される
ブール真偽値の集合を表現する.
事前宣言されたブール型は bool
である.
bool
は,定義型 (defined type) である.
数値型 (numeric type) は整数値の集合または浮動小数点値たちの集合を表現する. 事前宣言されたアーキテクチャに依存しない数値型は以下である.
uint8 符号なし 8 ビット整数の集合 (`0`-`255`)
uint16 符号なし 16 ビット整数の集合 (`0`-`65535`)
uint32 符号なし 32 ビット整数の集合 (`0`-`4294967295`)
uint64 符号なし 64 ビット整数の集合 (`0`-`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 unit と同じサイズ
uintptr ポインター値のみ解釈ビットを格納するのに十分大きな符号なし整数
移植性の問題を回避するために,
すべての数値型は,定義型であり,
したがって,
uint8
のエイリアスである
byte
と,
int32
のエイリアスである
rune
を除いて区別する.
異なる数値型がひとつの式や代入において混在している場合,
明示的な変換が要求される.
例えば,
あるアーキテクチャにおいては同じ型を有する可能性があるが,
int32
と int
が異なる型である.
文字列型 (string type) は文字列値の集合を表現する.
文字列値は,(空を含めた) バイト列である.
バイトの数はその文字列の長さ (length) と呼ばれ,必ず非負である.
文字列は不変 (immutable) である.
つまり,一旦生成されると,文字列の内容を変更することは不可能である.
事前宣言された文字列型は string
である.
string
は,定義型 (defined type) である.
文字列 s
の長さは,
ビルトイン関数 len
を使って得られる.
文字列の長さは,
その文字列が定数であれば,
コンパイル時定数 (compile-time constant) である.
文字列のバイトには整数のインデックス 0
-len(s)-1
でアクセスできる.
アドレスの取得は不当となる.つまり,
s[i]
が i
バイト目であるとき, &s[i]
は不当である.
配列はある要素型 (element type) と呼ばれる単一の型の要素の番号付き列である. 要素の数はその配列の長さと呼ばれ,非負である.
ArrayType = "[" ArrayLength "]" ElementType .
ArrayLength = Expression .
ElementType = Type .
配列の長さは,
配列の型の一部である.
型 int
の値によって表現可能な非負定数でなければならない.
配列 a
の長さは,
ビルトイン関数 len
を使って得られる.
配列の要素は整数のインデックス 0
から len(a) - 1
でアクセスできる.
配列型はいつでも 1 次元であるが,
多次元型を形成するように構成できる.
[32]byte
[2*N] struct { x, y int32 }
[1000]*float64
[3][5]int
[2][2][2]float64 // [2]([2]([2]float64)) と同じ
スライス (slice) は
基底配列 (underlying array) の連続したセグメントの記述子であり,
その配列の要素に番号付の列でアクセスできる.
スライス型 (slice type) は,その要素型の配列のすべてのスライスの集合を表す.
要素の数はそのスライスの長さ (length) と呼ばれ,必ず非負である.
初期化されていないスライスの値は nil
である.
SliceType = "[" "]" ElementType .
スライス s
の長さは
ビルトイン関数 len
を使って得られる.
配列と異なり,実行中に変化する.
スライスの要素は,
整数のインデックス 0
から len(s) - 1
でアクセスできる.
ある要素のスライスでのインデックスは,
基底配列における同じ要素のインデックスよりも小さい場合がある.
スライスは一度初期化されると, スライスの要素たちを保持する 基礎配列によって関連付けられる. それゆえ, スライスは,その基底配列とストレージを共有し, 同じ配列から生成される他のスライスもまた,ストレージを共有する. 対照的に, 異なる配列は,いつでも異なるストレージを表現する.
スライスの基底配列は,
スライスの終端を超えて拡張されうる.
容量 (capacity) はその拡張量である:
容量は,スライスの長さとそのスライスを超えた配列の長さの和である.
容量を超えた長さのスライスは,
元のスライスから
新しいスライスが元のスライスをスライスする @@@ことによって
その容量までの長さのスライスが生成できる.
スライス a
の容量は,
ビルトイン関数 cap(a)
を使って得られる.
(訳注:本質的ではないが,本家の typo ではないだろうか. a
-> s
)
ある与えられた要素型 T
の初期化されたスライス値は,
ビルトイン関数 make
を使って得られる.
make
はスライス型と,
長さ,および,オプションで容量を指定するパラメーターを受け取る.
make
によって生成されたスライスは
常に復帰されたスライス値を参照する新しい隠し配列が割り当てられる.
つまり,
make([]T, length, capacity)
を実行すると, 配列を割り当てて,それをスライスして得られるものと同じスライスを生成する. つまり,以下の 2 つの式は等価である.
make([]int, 50, 100)
new([100]int)[0:50]
配列のように スライスはいつも 1 次元である. しかし,多次元のオブジェクトを構築するために構成できる. 配列の配列では,内部配列は,構造上,常に同じ長さである. しかし,スライスのスライス(または,スライスの配列)では, 内部スライスの長さは動的に変化する. さらに,内部スライスは個別に初期化する必要がある.
構造体はフィールド (field) と呼ばれる名前付き要素の列である.
各フィールドには名前と型がある.
フィールド名は明示的に指定される (IdentifierList
) か,
暗黙的に指定される (EmbeddedField
).
構造体内では,
ブランクでないフィールド名は一意でなければならない (MUST).
StructType = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ "*" ] TypeName .
Tag = string_lit .
// 空の構造体
struct {}
// 6 つのフィールドをもつ構造体
struct {
x, y int
u float32
_ float32 // パディング
A *[]int
F func()
}
型で宣言されているが,明示的なフィールド名がないフィールドは
埋め込みフィールド (embedded field) と呼ばれる.
埋め込みフィールドは,
型名 T
として指定されるか,
インターフェースでない型名 *T
へのポインターとして指定されなければならず,
T
自身はポインタータイプでない場合がある.
非修飾型名はフィールド名として機能する.
// 型 T1, *T2, P.T3, *P.T4 の型の 4 つの埋め込みフィールドをもつ構造体
struct {
T1 // フィールド名は T1
*T2 // フィールド名は T2
P.T3 // フィールド名は T3
*P.T4 // フィールド名は T4
x, y int // フィールド名は x と y
}
次の宣言は, フィールド名が構造体型内で一意でないため, 不当である:
struct {
T // 埋め込みフィールド *T, *P.T と衝突
*T // 埋め込みフィールド T, *P.T と衝突
*P.T // 埋め込みフィールド T, *T と衝突
}
構造体 x
の埋め込みフィールドの
フィールドやメソッド f
は
もし,x.f
がフィールドやメソッド f
を表記される正当なセレクタである場合,
x
は昇格された (promoted) と呼ばれる.@@@
昇格フィールド (promoted field) は 構造体の複合リテラルにおいて フィールド名として使用できないことを除いて, 構造体の通常のフィールドのように機能する.
構造体型 S
と定義型 T
が与えられたとき,
次のように
昇格されたメソッドは
構造体のメソッド集合に含まれる.
S
が埋め込みフィールドT
を含む場合,S
と*S
のメソッド集合はどちらも レシーバーT
と昇格されたメソッドに含まれる.*S
のメソッド集合もまた,レシーバー*T
に 昇格されたメソッドが含まれる.S
が埋め込みフィールド*T
を含む場合,S
と*S
のメソッド集合はどちらも,レシーバT
または*T
に 昇格されたメソッドが含まれる.
フィールド宣言は, オプションである文字列リテラルタグ (tag) に続く場合がある. これは,対応するフィールド宣言内のすべてのフィールドの属性になる. 空のタグ文字列は,タグがない場合と同値である. タグは, リフレクションインターフェース を通して表示され, 構造体の型同一性 @@@ (type identity) に参加する@@@が, そうでなければ,無視される.
struct {
x, y float64 "" // 空文字列タグは,タグがない場合と似ている
name string "タグとして,任意の文字列が許される"
_ [4]byte "コレハコウゾウタイフィールドデハアリマセン"
}
// TimeStamp プロトコルバッファ (protocol buffer) に対応する構造体
// タグ文字列は,プロトコルバッファのフィールド番号を定義する;
// タグ文字列は,リフレクトパッケージで概説されている規則に従う
struct {
microsec uint64 `protobuf:"1"`
serverIP6 uint64 `protobuf:"2"`
}
ポインター型 (pointer type) は
与えられた型の変数へのすべてのポインターの集合を表し,
ポインターの基本型 (base type) と呼ばれる.
未初期化のポインターの値は nil
である.
PointerType = "*" BaseType .
BaseType = Type .
*Point
*[4]int
関数型 (function type)
は同じパラメーター (parameter) と復帰型 (result type) をもつすべての関数の集合を表す.
関数型の未初期化の変数の値は nil
である.
FunctionType = "func" Signature .
Signature = Parameters [ Result ] .
Result = Parameters | Type .
Parameters = "(" [ ParameterList [ "," ] ] ")" .
ParameterList = ParameterDecl { "," ParameterDecl } .
ParameterDecl = [ IdentifierList ] [ "..." ] Type .
パラメーターや復帰値のリストでは,
名前 (IdentifierList
) はすべて存在するか,すべて存在しないかのどちらかである (MUST).
存在する場合,各名前は指定された型の一つのアイテム (パラメーターまたは結果)を表し,
シグネチャーにおけるすべての非ブランクな名前は一意でなければならない.
存在しない場合,各型は,その型のひとつのアイテムを表す.
正確に 1 つだけの無名の復帰であり,丸括弧なしの型で書かれる場合を除いて,
パラメーターと結果のリストは,いつでも丸括弧で囲まれる.
関数シグネチャーの最後のパラメーターは,
...
を接頭辞にもつ型を持つ場合がある.
そのようなパラメーターをもつ関数は可変長引数 (variadic) と呼ばれ,
そのパラメーターとして,0個以上の引数として呼び出される.
訳注:@@@ 復帰パラメーター
func()
func(x int) int
func(a, _ int, z float32) bool
func(a, b int, z float32) (bool)
func(prefix string, values ...int)
func(a, b int, z float64, opt ...interface{}) (success bool)
func(int, int, float64) (float64, *[]int)
func(n int) func(p *T)
インターフェース型 (interface type) は
インターフェース (interface) と呼ばれるメソッド集合を指定する.
インターフェース型の変数は,
そのインターフェースの任意のスーパーセットであるメソッド集合の
任意の型の値を格納する.
そのような型はインターフェースの実装 (implement the interface) という.
インターフェース型の未初期化な変数の値は nil
である.
InterfaceType = "interface" "{" { ( MethodSpec | InterfaceTypeName ) ";" } "}" .
MethodSpec = MethodName Signature .
MethodName = identifier .
InterfaceTypeName = TypeName .
インターフェース型は, メソッド仕様を介してメソッドを明示的に指定するか, インターフェース型名を介して, 他のインターフェースのメソッドを埋め込むことができる.
// 単純な File インターフェース
interface {
Read([]byte) (int, error)
Write([]byte) (int, error)
Close() error
}
各明示的に指定されたメソッドの名前は一意であり,非ブランクでなければならない.
interface {
String() string
String() string // 不当: String は一意でない
_(x int) // 不当: メソッドは非ブランク名でなければならない
}
複数の型がインターフェースで実装される.
例えば, 2 つの型 S1
と S2
が次のメソッド集合を持つ場合,
func (p T) Read(p []byte) (n int, err error)
func (p T) Write(p []byte) (n int, err error)
func (p T) Close() error
(ここで,T
は S1
または S2
を表す)
S1
と S2
の他のメソッドをもつ,または,共有するかに関係なく,
File
インターフェースは S1
と S2
の両方で実装される.
型は,そのメソッドの部分集合を含むインターフェースを実装するため, いくつかの異なるインターフェースを実装できる. 例えば,すべての型が空インターフェース (empty interface) を実装する.
interface{}
同様に,
Locker
と呼ばれるインターフェースを定義する型宣言で現れる
このインターフェース仕様を考える.
type Locker interface {
Lock()
Unlock()
}
もし,S1
と S2
が次を実装すると
func (p T) Lock() { … }
func (p T) Unlock() { … }
S1
と S2
は Locker
インターフェースと File
インターフェースを実装する.
インターフェース T
は
メソッド仕様の代わりに
(修飾された可能性のある) インターフェース型名 E
を使用できる.
これは,T
における埋め込み (embedding) インターフェース E
と呼ばれる.
T
のメソッド集合は
T
の明示的に宣言されたメソッド集合と,
T
の埋め込みインターフェースのメソッド集合の和集合である.
type Reader interface {
Read(p []byte) (n int, err error)
Close() error
}
type Writer interface {
Write(p []byte) (n int, err error)
Close() error
}
// ReadWriter のメソッドは Read, Write, Close である
type ReadWriter interface {
Reader // `ReadWriter` のメソッド集合は `Reader` のメソッドを含む
Writer // `ReadWriter` のメソッド集合は `Writer` のメソッドを含む
}
メソッド集合の和集合には, 各メソッド集合の (エクスポート (export) されたおよびされていない) メソッドが一度だけ含まれ, 同じ名前のメソッドは,同一のシグネチャーを持たなければならない.
type ReadCloser interface {
Reader // `ReadWriter` のメソッド集合は `Reader` のメソッドを含む
Close() // 不当: `Reader.Close` と `Close` のシグネチャーは異なる
}
インターフェース型 T
は
それ自身や T
が埋め込まれた任意の型を再帰的に埋め込むことはできない.
// 不当: Bad はそれ自身を埋め込めない
type Bad interface {
Bad
}
// 不当: Bad1 は Bad2 を使ってそれ自身を埋め込むことはできない
type Bad1 interface {
Bad2
}
type Bad2 interface {
Bad1
}
訳注: 「union」が 2 回強調
マップ (map) は
要素型 (element type) と呼ばれる要素の型の順序付けられていないグループであり,
キー型 (key type) と呼ばれる別の型の一意キーの集合によってインデックスがつけられる.
未初期化のマップの値は nil
である.
MapType = "map" "[" KeyType "]" ElementType .
KeyType = Type .
比較演算子 ==
と !=
はキー型のオペランドに定義されなければならない.
したがって,キー型は
関数,マップ,スライスであってはならない.
キー型がインターフェース型である場合,
これらの比較演算子は,
動的にキー値に対して定義されなければならない.
失敗すると,
ランタイムパニックが発生する.
map[string]int
map[*T]struct{ x, y float64 }
map[string]interface{}
マップの要素の数は長さと呼ばれる.
マップ m
に対して,長さは
ビルトイン関数 len
を使って得られ,
これは実行中に変化する.
要素は,実行中に代入によって追加し,
インデックス式によって取得できる.
ビルトイン関数 delete
によって削除できる.
(訳注:m
は使っていないのは本家のバグ?)
新しい,空のマップ値は,
ビルトイン関数 make
を使って生成でき,
make
はマップ型とオプションで容量ヒントを引数にとる.
make(map[string]int)
make(map[string]int, 100)
初期容量は,そのサイズを制限しない.
マップは,マップに格納されるアイテムの数に合わせて成長する.
nil
マップは,要素が追加されないことを除いて,空のマップと等価である.
訳注:急に item という表現に. アイテム=要素
チャンネル (channel) は
指定された要素型を送信と受信する値によって通信する同時に (concurrently) 実行する関数のためのメカニズムを提供する.
未初期化のチャンネルの値は nil
である.
ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .
オプションである <-
演算子は
チャンネルの方向,つまり,送信または受信を指定する.
もし,方向が指定されなかった場合,
チャンネルは双方向 (bidirectional) になる.
チャンネルは代入か明示的な変換によって送信のみか受信のみに制限される場合がある.
chan T // 型 T の値の送受信に使用できる
chan<- float64 // float64 値の送信にのみ使用できる
<-chan int // int 値の受信にのみ使用できる
<-
演算子は可能な限り左の chan
に関連付けられる.
chan<- chan int // chan<- (chan int) と同じ
chan<- <-chan int // chan<- (<-chan int) と同じ
<-chan <-chan int // <-chan (<-chan int) と同じ
chan (<-chan int)
新しい,初期化されたチャンネル値は,
ビルトイン関数 make
を使って生成でき,
make
はチャンネル型とオプションで容量を引数にとる.
make(chan int, 100)
容量(要素数)はチャンネル内のバッファサイズを設定する.
容量がゼロまたは存在しない場合,
チャンネルはバッファリングされておらず,
送信者と受信者の両方が準備できたときにのみ,通信は成功する.
それ以外の場合,
チャンネルはバッファリングされており,
バッファがいっぱいでない (送信時)か,
空でない(受信時)場合には,
ブロックせずに通信が成功する.
nil
チャンネルは通信の準備ができることはない.
チャンネルはビルトイン関数 close
を使って閉じられる.
受信演算子の多値代入形式はチャンネルが閉じられる前に受信値が送信されたかどうかを報告する.
単一のチャンネルは送信文,受信演算,
ビルトイン関数 cap
と len
の呼び出しにおいて,
さらなる同期なしで任意の数のゴルーチンによって,使用できる.
チャンネルは先入れ先出し (FIFO: first-in-firs-out) キューとして機能する.
例えば,
あるゴルーチンがチャンネルで値を送信し,
別のゴルーチンがその値を受信すると,
値は送信した順番で受信される.
2つの型は同一 (identical) または異なる (different) ものである.
定義型はいつでも他の型と異なる. それ以外の場合, 基底型リテラルが構造的に同一な場合, 2 つの型は同一である. つまり, 2 つの型は,同一のリテラル構造を持ち, 対応するコンポーネントたちは,同一の型をもつ.
詳細には,
- 2 つの配列型が,同一の要素型を持ち,同じ配列長である場合,それらは同一である.
- 2 つのスライス型が同一の要素型を持つ場合,それらは同一である.
- 2 つの構造体型が同じフィールド列を持ち,対応するフィールドが同じ名前,同一の型,同一のタグをもつ場合,それらは同一である.異なるパッケージのエクスポートされていないフィールド名はいつも異なる.
- 2 つのポインター型は,同一の基本型を持つ場合,それらは同一である.
- 2 つの関数型は, 同じ数のパラメーターと復帰値をもち, 対応するパラメーターと復帰型は同一であり, どちらの関数も可変長引数であるかそうでないかのどちらかであるとき, それらは同一である. パラメーターと復帰値の名前は一致する必要はない.
- 2 つのインターフェース型は, 同じ名前と一意関数型をもつメソッドの集合をもつとき,それらは同一である. 異なるパッケージのエクスポートされないメソッド名はいつも異なる. メソッドの順序は関係ない.
- 2 つのマップ型は,一意キー型と要素型が一致するとき,それらは同一である.
- 2 つのチャンネルは,一意要素型と,方向が一致するとき,それらは同一である.
次の宣言を考える.
type (
A0 = []string
A1 = A0
A2 = struct{ a, b int }
A3 = int
A4 = func(A3, float64) *A0
A5 = func(x int, _ float64) *[]string
)
type (
B0 A0
B1 []string
B2 struct{ a, b int }
B3 struct{ a, c int }
B4 func(int, float64) *B0
B5 func(x int, y float64) *A1
)
type C0 = B0
これらは同一である.
A0, A1, and []string
A2 and struct{ a, b int }
A3 and int
A4, func(int, float64) *[]string, and A5
B0 and C0
[]int and []int
struct{ a, b *T5 } and struct{ a, b *T5 }
func(x int, y float64) *[]string, func(int, float64) (result *[]string), and A5
B0
と B1
は異なる型定義によって生成された新しい型なので,
B0
と B1
は異なる;
B0
は []string
と異なるので,
func(int, float64) *B0
とfunc(x int, y float64) *[]string
は異なる.
次の条件のうち一つでも満たす場合,
値 x
は型 T
の変数に代入可能 (assignable) である.
(x
は T
に代入可能)
x
の型はT
と同一である.x
の型V
とT
は同一の基底型をもち,V
とT
の少なくともひとつは定義型ではない.T
はインターフェース型であり,x
はT
を実装している.x
は双方向のチャンネル値であり,T
はチャンネル型であり,x
の型V
とT
は同一の要素型をもち,V
かT
の少なくともひとつは定義型ではない.x
は事前宣言された識別子nil
であり,T
はポインター型,関数型,スライス型,マップ型,チャンネル型,インターフェース型のいずれかである.x
は型T
の値によって表現可能な型なし定数である.
次の条件のうち,ひとつの条件を満たすとき,
定数 x
は 型 T
の値によって表現可能である.
x
はT
によって決定される値の集合に属する.T
は浮動小数点型であり,x
がオーバーフローなしでT
の精度に丸められる. 丸めは,IEEE 754 の偶数への丸め (round-to-even) 規則が使用されるが, IEEE 負ゼロは更に符号なしゼロに簡単化される. 定数値は IEEE 負ゼロ,NaN
, 無限大にはならないことに注意する.T
は複素数型で,x
のコンポーネントreal(x)
とimage(x)
はT
のコンポーネント型 (float32
またはfloat64
) の値として表現可能である.
x T x は T の値によって表現可能である, なぜなら
'a' byte 97 は byte 値の集合に属する
97 rune ルーンは int32 のエイリアスであり, 97 は 32ビット整数の集合に属する
"foo" string "foo" は文字列値の集合に属する
1024 int16 1024 は 16ビット整数の集合に属する
42.0 byte 42 は符号なし 8ビット整数の集合に属する
1e10 uint64 10000000000 は符号なし 64ビット整数の集合に属する
2.718281828459045 float32 2.718281828459045 は float32 値の集合に属する 2.7182817 に丸められる
-1e-1000 float64 -1e-1000 は IEEE -0.0 に丸められ,さらに 0.0 に簡略化される
0i int 0 は整数値である
(42 0i) float32 42.0 (0 の虚部をもつ) は float32 値の集合に属する
x T x は T の値によって表現不可能である, なぜなら
0 bool 0 はブール値の集合に属しない
'a' string 'a' はルーンであり,文字列値の集合に属しない
1024 byte 1024 は符号なし 8ビット整数の集合に属さない
-1 uint16 -1 は符号なし 16びと整数の集合に属さない
1.1 int 1.1 は整数値ではない
42i float32 (0 42i) は float32 値の集合に属さない
1e1000 float64 1e1000 は丸め後に IEEE Inf にオーバーフローする
ブロック (block) は, 波括弧 (brace brackets) 内の宣言と文の列(空の場合もある)である.
Block = "{" StatementList "}" .
StatementList = { Statement ";" } .
ソースコード内の明示的なブロックに加えて, 以下の暗黙的なブロックもある.
- ユニバースブロック (universe block) はすべての Go ソースコードを包含する.
- 各パッケージは,そのパッケージのすべての Go ソースコードに含まれるパッケージブロック (package block) をもつ.
- 各ファイルは,そのファイル内のすべての Go ソーステキストを含むファイルブロック (file block) をもつ.
- 各
if
文,for
文,switch
文は,それ自身の暗黙的なブロックに含まれると見なされる. switch
文とselect
文の各句は暗黙的なブロックとして機能する.
ブロックはネストし,スコープに影響する.
宣言 (declaration) は,非ブランクな識別子を定数, 型, 変数, 関数, ラベル, パッケージへバインドする (bind). プログラム内のすべての識別子は,宣言される必要がある. 同じブロック内で識別子を 2 回宣言できず, ファイルとパッケージの両方のブロックで識別子を宣言できない.
ブランク識別子は,
宣言内の他の識別子と同じように使用できるが,
バインドされず,したがって,宣言もされない.
パッケージブロックにおいて,
識別子 init
は
init
関数の宣言にのみ使用され,
ブランク識別子と同様に,新しいバインドを導入しない.
Declaration = ConstDecl | TypeDecl | VarDecl .
TopLevelDecl = Declaration | FunctionDecl | MethodDecl .
宣言された識別子のスコープ (scope) は, 識別子が指定された定数,型,変数,関数,ラベル,パッケージを示すソーステキストの範囲である.
Go は,ブロックを用いて, 字句的にスコープされる.
- 事前宣言のスコープは,ユニバースブロックである.
- トップレベルで宣言された定数,型,変数,関数 (メソッドは除く)を示す識別子のスコープは, パッケージブロックである.
- インポートされたパッケージのパッケージ名のスコープは, インポート宣言を含むファイルのファイルブロックである.
- メソッドレシーバー,関数パラメーター,復帰変数を示す識別子のスコープは, 関数本体である.
- 関数内で宣言された定数と変数識別子のスコープは,
ConstSpec
とVarSpec
(ShortVarDecl
) の終わりから開始し, もっとも内側に含まれるブロックの終端で終了する. - 関数内で宣言された型識別子のスコープは,
TypeSpec
内のidentifier
から開始し, もっとも内側に含まれるブロックの終端で終了する.
ブロック内で宣言された識別子は, 内部ブロック (inner block)で再宣言できる. 内部ブロックの識別子は,スコープに含まれるが, 内部ブロックの識別子は, 内部宣言によって宣言されたエンティティ (entity) を示す.
パッケージ句 (package clause) は,宣言ではない. パッケージ名はスコープに現れることはない. その目的は,同じパッケージに属するファイルを特定し, インポート宣言のデフォルトのパッケージを指定することである.
ラベル (label) は,
ラベル文 (labeled statement) によって宣言され,
break
文,
continue
文,
goto
文で使用される.
使用されないラベルを定義することは,不当である.
他の識別子と異なり,
ラベルはブロックスコープではなく,
ラベルでない識別子と衝突しない.
ラベルのスコープはラベルが宣言された関数の本体であり,
ネストされた関数の本体は除く.
ブランク識別子 (blank identifier) はアンダースコア _
によって表現される.
ブランク識別子は,通常の (非ブランクな)識別子の代わりに匿名のプレースホルダー
(訳注:一時的に保管するだけの変数)として機能し,
宣言,オペランド,代入で特別な意味を持つ.
次の識別子は,ユニバースブロック (universe block) で暗黙的に宣言されている.
型:
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
他のパッケージからのアクセスを許可するために, 識別子は,エクスポート (export) できる. 次の 2 つの条件を満足する場合, 識別子はエクスポートされる:
- 識別子名の最初の文字は,Unicode 大文字 (Unicode クラス "Lu") であり,
- 識別子は,パッケージブロックで宣言されるか,フィールド名であるか,メソッド名である.
他のすべての識別子はエクスポートされない.
識別子の集合が与えられたとき, 集合内における他のすべての識別子と異なるとき, 識別子は,一意 (unique) であると呼ぶ. 2 つの識別子が, 「スペルが異なる」か, 「異なるパッケージに現れ, エクスポートされない」とき, その識別子たちは異なる.
定数宣言 (constant declaration) 識別子のリストに定数式 (constant expression) のリストの値をバインドする. 識別子の数は,定数式の数と一致しなければならず, 左辺の n 番目の識別子に, 右辺の n 番目の定数式の値がバインドされる.
ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] .
IdentifierList = identifier { "," identifier } .
ExpressionList = Expression { "," Expression } .
型が示された場合, すべての定数は指定された型をとり, 式はその型に代入可能でなければならない. 型が省略された場合, 定数は対応する式の個々の型をとる. 式の値が型なし定数の場合, 宣言された定数は,型なしのままであり, 定数識別子は,定数値を示す. 例えば, 式が浮動小数点リテラルである場合, 定数の小数部がゼロであったとしても定数識別子は,浮動小数点定数を示す.
const Pi float64 = 3.14159265358979323846
const zero = 0.0 // 型なし浮動小数点定数
const (
size int64 = 1024
eof = -1 // 型なし整数定数
)
const a, b, c = 3, 4, "foo" // a = 3, b = 4, c = "foo", 型なし整数定数と型なし文字列定数
const u, v float32 = 0, 3 // u = 0.0, v = 3.0
丸括弧で囲まれた (parenthesized) const
宣言リスト内では,
最初の ConstSpec
以外の式リストは省略できる.
このような空の式リストは,
最初の先行する空でない式リストとその型のテキスト置換と等価である.
それゆえに,
式リストの省略は,
前の式リストの繰り返しを繰り返すことと等価である.
識別子の数は,
前のリストにおける式の数と等価である.
このメカニズムは,
iota
定数生成器を使うことで,
連続値の容易な宣言を可能とする.
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Partyday
numberOfDays // この定数はエクスポートされない
)
定数宣言内で,
事前宣言された識別子 iota
は,
連続した型なし整数定数を表現する.
その値は,
定数宣言におけるそれぞれの ConstSpec
ゼロから始まるインデックスである.
関連する定数の集合を作成するために使用できる.
const (
c0 = iota // c0 == 0
c1 = iota // c1 == 1
c2 = iota // c2 == 2
)
const (
a = 1 << iota // a == 1 (iota == 0)
b = 1 << iota // b == 2 (iota == 1)
c = 3 // c == 3 (iota == 2, 使用されない)
d = 1 << iota // d == 8 (iota == 3)
)
const (
u = iota * 42 // u == 0 (型なし整数定数)
v float64 = iota * 42 // v == 42.0 (float64 定数)
w = iota * 42 // w == 84 (型なし整数定数)
)
const x = iota // x == 0
const y = iota // y == 0
定義により,
同一の ConstSpec
内での
iota
の複数回の使用は,すべて同じ値をもつ.
const (
bit0, mask0 = 1 << iota, 1<<iota - 1 // bit0 == 1, mask0 == 0 (iota == 0)
bit1, mask1 // bit1 == 2, mask1 == 1 (iota == 1)
_, _ // (iota == 2, 使用されない)
bit3, mask3 // bit3 == 8, mask3 == 7 (iota == 3)
)
最後の例は,最後の空でない式リストの暗黙的な繰り返しを利用している.
型宣言 (type declaration) は, 型名 (type name) である識別子に型 (type) をバインドする. 型宣言は, エイリアス宣言 (alias declarations) と型定義 (type definitions) の 2 つの形式がある.
TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
TypeSpec = AliasDecl | TypeDef .
エイリアス宣言 (alias declaration) は識別子に与えられた型をバインドする.
AliasDecl = identifier "=" Type .
識別子のスコープ内では, その識別子は,与えられた型のエイリアスとして機能する.
type (
nodeList = []*Node // nodeList と []*Node は同じ型である
Polar = polar // Polar と polar は同じ型を示す
)
型定義 (type definition) は,指定された型と同じ基底型と演算をもつ, 新しく,別の型を生成し, それの新しい型を識別子にバインドする.
TypeDef = identifier Type .
新しい型は定義型 (defined type) と呼ばれる. 新しい型は, 生成元となる型を含む, 他のあらゆる型とは, 異なる.
type (
Point struct{ x, y float64 } // Point と struct{ x, y float64 } は異なる型である
polar Point // polar と Point は異なる型を示す
)
type TreeNode struct {
left, right *TreeNode
value *Comparable
}
type Block interface {
BlockSize() int
Encrypt(src, dst []byte)
Decrypt(src, dst []byte)
}
定義型は,それに関連付けられたメソッドをもつことができる. 定義型は,指定された型にバインドされたメソッドは継承 (inherit) しないが, インターフェース型,または, 複合型の要素のメソッド集合は変更されない.
// Mutex は 2 つのメソッド (Lock, Unlock) をもつデータ型である.
type Mutex struct { /* Mutex フィールド */ }
func (m *Mutex) Lock() { /* Lock の実装 */ }
func (m *Mutex) Unlock() { /* Unlock の実装 */ }
// NewMutex は,Mutex と同じ構成であるが,そのメソッド集合は空である.
type NewMutex Mutex
// *Mutex を基底型とする,PtrMutex のメソッド集合は変更されないが,
// PtrMutex のメソッド集合は空である
type PtrMutex *Mutex
// *PrintableMutex のメソッド集合は埋め込みフィールド Mutex にバインドされた
// 2 つのメソッド 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)
}
変数宣言 (variable declaration) は 1 つ以上の変数を生成し, 対応する識別子を変数にバインドし, それぞれに型と初期値を与える.
VarDecl = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
var i int
var U, V, W float64
var k = 0
var x, y float32 = -1, -2
var (
i int
u, v, s = 2.0, 3.0, "bar"
)
var re, im = complexSqrt(-1)
var _, found = entries[name] // マップ検索; "found" にのみ興味がある
式のリスト (ExpressionList
) が与えられたら,
変数は以下の代入規則に従った式によって初期化される.
そうでなければ,各変数はゼロ値で初期化される.
型が指定されると,変数はその型になる.
型が指定されなければ,各変数は代入における対応する初期値の型が与えられる.
その値が型なし定数であれば,
最初に暗黙的にデフォルト型に変換される.
型なしブール値であれば,最初に暗黙的に bool
型に変換される.
事前宣言された値 nil
は,明示的な型を持たない変数の初期値として使用できない.
var d = math.Sin(0.5) // d は float64
var i = 42 // i は int
var t, ok = x.(T) // t は T, ok は bool
var n = nil // 不当
実装上の制限: コンパイラは, 関数本体内で宣言された変数が, 関数内で使用されない場合, 不当とすることがある.
簡潔な変数宣言 (short variable declaration) は次の文法を使用する.
ShortVarDecl = IdentifierList ":=" ExpressionList .
これは,初期値があるが型がない通常の変数宣言の省略形である.
"var" IdentifierList = ExpressionList .
i, j := 0, 10
f := func() int { return 7 }
ch := make(chan int)
r, w, _ := os.Pipe() // os.Pipe() は Files の接続ペア(r, w)と error を返す
_, y, _ := coord(p) // coord() は 3 つの値を返す; しかし, y 軸にしか興味がない
通常の変数宣言とは異なり, 簡潔な変数宣言では, 同じブロック内 (または,そのブロックが関数本体であればパラメーターリスト) で前に宣言された同じ型の変数, そして,少なくとも 1 つの非ブランクな変数が新しい場合, 変数を再宣言 (redeclare) できる. 結果として,再宣言は多値の簡潔な変数宣言においてのみ使用される. 再宣言では新しい変数は導入されず, 新しい値が元の変数に代入される.
field1, offset := nextField(str, 0)
field2, offset := nextField(str, offset) // オフセットを再宣言する
a, a := 1, 2 // 不当: a が他の場所で宣言されている場合,a の二重宣言または新規変数なし
簡潔な変数宣言は,関数内でのみ現れる.
if
文,
for
文,
switch
文の初期化のようにローカル一時変数の宣言に使用できる.
訳注:a tour of go 翻訳では,短い変数宣言
関数宣言 (function declaration; FunctionDecl
) は
関数名 (function name) である識別子に,
関数をバインドする.
FunctionDecl = "func" FunctionName Signature [ FunctionBody ] .
FunctionName = identifier .
FunctionBody = Block .
関数のシグネチャーが復帰パラメーターたちを宣言する場合,
関数本体 (FunctionBody
) の文のリストは,
終端文 (terminating statement) で終了しなければならない.
func IndexRune(s string, r rune) int {
for i, c := range s {
if c == r {
return i
}
}
// 無効: return 文がない
}
関数は,その本体なしで宣言される場合がある. そのような宣言は, アセンブリルーチン (assembly routine) のように Go の外で実装された関数のシグネチャーを提供する.
func min(x int, y int) int {
if x < y {
return x
}
return y
}
func flushICache(begin, end uintptr) // 外部で実装される
メソッド (method) はレシーバー (receiver) をもつ関数である. メソッド宣言 (method declaration) はメソッド名 (method name) である識別子に, メソッドをバインド (bind) し, レシーバーの基本型 (base type) にメソッドを関連付ける.
MethodDecl = "func" Receiver MethodName Signature [ FunctionBody ] .
Receiver = Parameters .
レシーバーは,メソッド名の前にある追加のパラメーター節で指定される.
そのパラメーター節は,ひとつの不可変数引数で,そのレシーバーを宣言する.
その型は,定義型 T
,または,定義型 T
へのポインターでなければならない.
T
はレシーバー基本型 (base type)と呼ばれる.
レシーバー基本型は,ポインター型またはインターフェース型にはできず,
メソッドとして,同じパッケージ内で定義されなければならない.
そのメソッドはレシーバー基本型にバインド (bind) されると言い,
メソッド名は,型 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)
しかしながら,この方法で宣言された関数はメソッドではない.
式 (expression) は. 演算子と関数をオペランドへの適用によって値の計算を指定する.
オペランド (operand) は式における基本値を示す. オペランドは, リテラル, または, 定数, 変数, 関数を示す (場合によっては修飾された) 非ブランクな識別子,または, 丸括弧で囲まれた式である.
Operand = Literal | OperandName | "(" Expression ")" .
Literal = BasicLit | CompositeLit | FunctionLit .
BasicLit = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
OperandName = identifier | QualifiedIdent .
修飾識別子 (qualified identifier) はパッケージ名を前頭において修飾された識別子である. パッケージ名と識別子はどちらもブランクにできない.
QualifiedIdent = PackageName "." identifier .
修飾識別子は, インポートされなければならない, 別のパッケージの識別子にアクセスする. 識別子は,エクスポートされており, そのパッケージのパッケージブロックで宣言されていなければならない.
math.Sin // パッケージ math の Sin 関数を示す
複合リテラル (composite literal, CompositeLit
) は,
構造体,配列,スライス,マップの値を構築し,
複合リテラルが評価されるたびに新しい値を生成する.
複合リテラルは,
リテラルの型 LiteralType
と,
波括弧で囲まれた要素のリスト LiteralValue
から成る.
各要素は,オプションとして対応するキー Key
を要素の前に置くことがある.
CompositeLit = LiteralType LiteralValue .
LiteralType = StructType | ArrayType | "[" "..." "]" ElementType |
SliceType | MapType | TypeName .
LiteralValue = "{" [ ElementList [ "," ] ] "}" .
ElementList = KeyedElement { "," KeyedElement } .
KeyedElement = [ Key ":" ] Element .
Key = FieldName | Expression | LiteralValue .
FieldName = identifier .
Element = Expression | LiteralValue .
LiteralType
の基底型は,
構造体型,配列型,スライス型,マップ型でなければならない
(型が TypeName
として与えられた場合を除いて,文法はこの制約を強制する).
要素とキーの型は,
リテラル型のそれぞれのフィールド型,要素型,キー型に代入可能でなければならない.
さらなる変換はない.
キーは,
構造体のフィールド名,
配列リテラルとスライスリテラルのインデックス,
マップリテラルのキーとして解釈される.
マップリテラルの場合,
すべての要素はキーと持たなければならない.
同じフィールド名,または,定数キー値をもつ複数の要素を指定した場合,エラーになる.
非定数マップキーの場合は,評価順序節を参照されたい.
構造体リテラルの場合,以下の規則が適用される:
- キーは,構造体型内で宣言されたフィールド名でなければならない.
- キーを全く含まない要素リスト
ElementList
は, フィールドが宣言された順序で 各構造体フィールドに対する要素がリストされなければならない. - ある要素がキーをもつならば,すべての要素がキーをもたなければならない.
- キーを含む要素リストは, 構造体フィールドの要素が必要ない. 省略されたフィールドにはそのフィールドのゼロ値を取得する.
- リテラルは要素リストを省略してもいい.そのようなリテラルは,その型のゼロ値が評価される.
- 異なるパッケージに属する構造体のエクスポートされないフィールドの要素を指定するとエラーになる.
次の宣言が与えられたとき,
type Point3D struct { x, y, z float64 }
type Line struct { p, q Point3D }
次のように書いてもいい.
origin := Point3D{} // Point3D のゼロ値
line := Line{origin, Point3D{y: -4, z: 12.3}} // line.q.x にゼロ値
配列リテラルとスライスリテラルの場合,以下の規則が適用される:
- 各要素は,配列の位置を示す整数インデックスが関連付けられている.
- キーをもつ要素は,そのインデックスとしてそのキーを使用する.
キーは
int
型の値で表される非負定数でなければならない. - キーを持たない要素は,その前の要素のインデックスに 1 を加えた値を使用する. 最初の要素がキーを持たなければ,そのインデックスは 0 である.
複合リテラルのアドレスを取得すると, リテラル値で初期化された一意の変数へのポインターが生成される.
var pointer *Point3D = &Point3D{y: 1000}
スライス型とマップ型のゼロ値は同じ型の空の値に初期化されたものを同じではないことに注意されたい.
したがって,
空のスライスの複合リテラルのアドレス,または,
空のマップの複合リテラルのアドレスを取得することは,
new
によって新しいスライス値やマップ値を割り当てることとは同じ効果ではない.
p1 := &[]int{} // p1 []int{} 値の空のスライスで長さ 0 のスライスをポイントする
p2 := new([]int) // p2 は nil 値をもつ未初期化で長さ 0 のスライスをポイントする
配列リテラルの長さはリテラル型において指定された長さである.
長さよりも少ない要素がリテラルで指定された場合,
不足している要素には,
配列の要素型に対するゼロ値が設定される.
配列のインデックスの範囲外となるインデックス値を要素に指定すると,
エラーになる.
表記 ...
は要素のインデックスの最大値に 1 を加えた配列の長さの配列を指定する.
buffer := [10]string{} // len(buffer) == 10
intSet := [6]int{1, 2, 3, 5} // len(intSet) == 6
days := [...]string{"Sat", "Sun"} // len(days) == 2
スライスリテラルは, 基底配列リテラル全体を記述する. したがって,スライスリテラルの長さと容量は要素のインデックスの最大値に 1 を加えたものとなる. スライスリテラルは,次の形式をもつ.
[]T{x1, x2, … xn}
配列に適用されるスライス操作の省略形は以下である.
tmp := [n]T{x1, x2, … xn}
tmp[0 : n]
T
を配列型,スライス型,または,マップ型とする.
T
の複合リテラル内で,
それ自身が複合リテラルであるような要素やマップキーは,
もし,それが T
の要素型,またはキー型と同一であるなら,
それぞれのリテラル型を削除できる.
同様に,
複合リテラルのアドレスであるような要素やキーは,
要素型,またはキー型が *T
であるとき,
&T
を削除できる.
[...]Point{ {1.5, -3.5}, {0, 0}} // [...]Point{Point{1.5, -3.5}, Point{0, 0}} と同じ
[][]int{ {1, 2, 3}, {4, 5}} // [][]int{[]int{1, 2, 3}, []int{4, 5}} と同じ
[][]Point{ { {0, 1}, {1, 2}}} // [][]Point{[]Point{Point{0, 1}, Point{1, 2}}} と同じ
map[string]Point{"orig": {0, 0}} // map[string]Point{"orig": Point{0, 0}} と同じ
map[Point]string{ {0, 0}: "orig"} // map[Point]string{Point{0, 0}: "orig"} と同じ
type PPoint *Point
[2]*Point{ {1.5, -3.5}, {}} // [2]*Point{&Point{1.5, -3.5}, &Point{}} と同じ
[2]PPoint{ {1.5, -3.5}, {}} // [2]PPoint{PPoint(&Point{1.5, -3.5}), PPoint(&Point{})} と同じ
// 訳注 GitHub Pages でのエラー回避のため,連続する { の間にスペースを追加している
TypeName
形式の LiteralType
を用いた複合リテラルが,
キーワードと if
文, for
文, switch
文のブロックの開き波括弧 {
の間にオペランドとして現れ,かつ,
複合リテラルが,丸括弧 ()
,角括弧 []
,波括弧 {}
で囲まれていないとき,
構文解析における曖昧性が発生する.
この稀なケースでは,
リテラルの開き波括弧 {
が文のブロックを導入するものとして,
誤って解析される.
この曖昧性を解決するために,
複合リテラルは,丸括弧の中に表示する必要がある.
if x == (T{a,b,c}[i]) { … }
if (x == T{a,b,c}[i]) { … }
有効な配列リテラル,スライスリテラル,マップリテラルの例:
// 素数のリスト
primes := []int{2, 3, 5, 7, 9, 2147483647}
// ch が vowel であれば vowels[ch] は true である
vowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}
// 配列 [10]float32{-1, 0, 0, 0, -0.1, -0.1, 0, 0, 0, -1}
filter := [10]float32{-1, 4: -0.1, -0.1, 9: -1}
// 平均律スケールの周波数 (Hz) (A4 = 440Hz)
noteFrequency := map[string]float32{
"C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83,
"G0": 24.50, "A0": 27.50, "B0": 30.87,
}
関数リテラル (function literal) は無名関数を表現する.
FunctionLit = "func" Signature FunctionBody .
func(a, b int, z float64) bool { return a*b < int(z) }
関数リテラルは,変数に代入されるか,直接呼び出される.
f := func(x, y int) int { return x y }
func(ch chan int) { ch <- ACK }(replyChan)
関数リテラルはクロージャー (closure) である. 関数リテラルは,周囲の関数で定義された変数を参照する場合がある. これらの変数は,周囲の関数と関数リテラルで共有され, アクセス可能である限り,生存する (survive).
一次式 (primary expression) は,単項式 (unary expression) と二項式 (binary expression) のオペランドである.
PrimaryExpr =
Operand |
Conversion |
MethodExpr |
PrimaryExpr Selector |
PrimaryExpr Index |
PrimaryExpr Slice |
PrimaryExpr TypeAssertion |
PrimaryExpr Arguments .
Selector = "." identifier .
Index = "[" Expression "]" .
Slice = "[" [ Expression ] ":" [ Expression ] "]" |
"[" [ Expression ] ":" Expression ":" Expression "]" .
TypeAssertion = "." "(" Type ")" .
Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
x
2
(s ".txt")
f(3.1415, true)
Point{1, 2}
m["foo"]
s[i : j 1]
obj.color
f.p[i].x()
パッケージ名ではない一次式 x
に対して,
セレクタ式 (selector expression)
x.f
は,値 x
(または,*x
; 下記参照) のフィールド f
,またはメソッド f
を示す.
識別子 f
は (フィールド,またはメソッド)セレクタ (selector) と呼ばれ,
ブランク識別子であってはならない.
セレクタ式の型は f
の型である.
x
がパッケージ名の場合は,修飾識別子を参照されたい.
セレクタ f
は,
型 T
のフィールド f
またはメソッド f
を示す場合と,
T
のネストされた埋め込みフィールドのフィールド f
かメソッド f
を参照する場合がある.
f
に到達するためにたどる埋め込みフィールドの数を
T
における f
の深さ (depth) と呼ばれる.
T
で宣言されたフィールド f
またはメソッド f
の深さはゼロである.
T
の埋め込みフィールド A
において宣言されたフィールド f
またはメソッド f
の深さは,
A
における f
の深さに 1 加えたものである.
以下のルールがセレクタに適用される.
T
がポインター型やインターフェース型 でないとするとき, 型T
または型*T
の値x
に対して,x.f
はf
が存在するとき,T
の深さがもっとも浅いフィールド,またはメソッドを示す. 深さが最も浅いf
がちょうど一つだけ存在しない場合, セレクタ表現は不当である.I
がインターフェース型であるとき,I
の値x
に対して,x.f
はx
の動的な値の名前f
をもつ実際のメソッドを示す. 名前f
のメソッドがI
のメソッド集合に存在しない場合, セレクタ表現は不当である.- 例外として,
x
の型が定義されたポインター型であり,(*x).f
がフィールドを示す (しかし,メソッドではない) 有効なセレクタ式である場合,x.f
は(*x).f
の省略形である. - 他のすべての場合で,
x.f
は不当である. x
がポインター型であり,値nil
を持ち,x.f
が構造体フィールドを表すとき,x.f
への代入や,x.f
の評価は, ランタイムパニックが発生する.x
がインターフェース型であり,値nil
を持つとき, メソッドx.f
の呼び出しやx.f
の評価は, ランタイムパニックが発生する.
例えば,以下の宣言が与えられたとき:
type T0 struct {
x int
}
func (*T0) M0()
type T1 struct {
y int
}
func (T1) M1()
type T2 struct {
z int
T1
*T0
}
func (*T2) M2()
type Q *T2
var t T2 // t.T0 != nil とする
var p *T2 // p != nil であり,(*p).T0 != nil とする
var q Q = p
次のように書ける.
t.z // t.z
t.y // t.T1.y
t.x // (*t.T0).x
p.z // (*p).z
p.y // (*p).T1.y
p.x // (*(*p).T0).x
q.x // (*(*q).T0).x (*q).x は有効なフィールドセレクタである
p.M0() // ((*p).T0).M0() M0 は *T0 レシーバーを想定する
p.M1() // ((*p).T1).M1() M1 は T1 レシーバーを想定する
p.M2() // p.M2() M2 は *T2 レシーバーを想定する
t.M2() // (&t).M2() M2 は *T2 レシーバーを想定する,呼び出し節を参照
しかし,以下は無効である.
q.M0() // (*q).M0 は有効であるが,フィールドセレクタである
M
が型 T
のメソッド集合に存在する場合,
T.M
は
M
の引数の最初に,
メソッドのレシーバーを追加したものを引数として,
通常の関数のように呼び出し可能である関数である.
MethodExpr = ReceiverType "." MethodName .
ReceiverType = Type .
構造体型 T
が 2 つのメソッド Mv
, Mp
をもつとする.
Mv
のレシーバーは型 T
である.
Mp
のレシーバーは型 *T
である.
type T struct {
a int
}
func (tv T) Mv(a int) int { return 0 } // value receiver
func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver
var t T
式
T.Mv
は, Mv
と等価であるが,
その最初の引数として明示的なレシーバーをもつ関数を生成する (yield).
それは次のシグネチャーをもつ.
func(tv T, a int) int
その関数は,明示的なレシーバーで通常呼び出されるため, 以下の 5 つの呼び出し方法は等価である.
t.Mv(7)
T.Mv(t, 7)
(T).Mv(t, 7)
f1 := T.Mv; f1(t, 7)
f2 := (T).Mv; f2(t, 7)
同様に,式
(*T).Mp
は,次のシグネチャーをもつ Mp
を表現する関数値を生成する.
func(tp *T, f float32) float32
値レシーバをもつメソッドの場合, 明示的なポインターレシーバーをもつ関数を導出する.
(*T).Mv
は,次のシグネチャーをもつ Mv
を表現する関数値を生成する.
func(tv *T, a int) int
そのような関数は,レシーバーを介して間接的に, 基底メソッドにレシーバーとして渡す値を生成する. メソッドは,関数呼び出しで渡されるアドレスの値を上書きしない.
最後のケースである, ポインターレシーバーメソッドのための値レシーバー関数は, ポインターレシーバーメソッドは値型のメソッド集合に属しないので, 不当である.
メソッドから派生した関数値は関数呼び出し構文で呼び出される.
レシーバーは,最初の引数として提供される.
つまり,f := T.Mv
が与えられたとき,
f
は t.f(7)
ではなく,f(t, 7)
のように呼び出される.
レシーバーをバインドする関数の構築には,
関数リテラルかメソッド値を使用する.
インターフェース型のメソッドの関数値を派生させることは正当である. 結果となる関数は,インターフェース型の明示的なレシーバをとる.
式 x
が静的な型 T
をもち,
M
が型 T
のメソッド集合に属する場合,
x.M
はメソッド値 (method value) と呼ばれる.
メソッド値 x.M
は
x.M
のメソッド呼び出しと同じ引数で呼び出される関数値である.
式 x
は評価され,メソッド値が評価される間保存される.
保存されたコピーは,
あとで実行される可能性のある任意の呼び出しでレシーバーとして使用される.
型 T
はインターフェース型,または,非インターフェース型である.
上記メソッド式で説明したように,
2 つのメソッド Mv
と Mp
をもつ構造体型 T
を考える.
Mv
のレシーバーは型 T
で,
Mp
のレシーバーは型 *T
である.
type T struct {
a int
}
func (tv T) Mv(a int) int { return 0 } // value receiver
func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver
var t T
var pt *T
func makeT() T
式
t.Mv
は次の型の関数値を生成する.
func(int) int
次の 2 つの呼び出し方法は等価である:
t.Mv(7)
f := t.Mv; f(7)
同様に,式
pt.Mp
は次の型の関数値を生成する.
func(float32) float32
セレクタと同様に,
ポインターを使って値レシーバーをもつ非インターフェースメソッドへの参照は,
自動的にそのポインターを間接参照 (dereference) する:
つまり,pt.Mv
は (*pt).Mv
と等価である.
メソッド呼び出しと同様に,
アドレス値を使ってポインターレシーバーをもつ非インターフェースメソッドへの参照は,
自動的にその値をアドレスをとる:
つまり,t.Mp
は (&t).Mp
と等価である.
f := t.Mv; f(7) // t.Mv(7) と同様
f := pt.Mp; f(7) // pt.Mp(7) と同様
f := pt.Mv; f(7) // (*pt).Mv(7) と同様
f := t.Mp; f(7) // (&t).Mp(7) と同様
f := makeT().Mp // 無効: makeT() の結果はアドレスではない
上の例は非インターフェース型を使用しているが, インターフェース型の値からメソッド値を生成することは正当である.
var i interface { M(int) } = myVal
f := i.M; f(7) // like i.M(7)
一次式
a[x]
は,配列, 配列へのポインター,スライス,文字列,または,マップ a
の
x
でインデックスされた要素を示す.
値 x
はそれぞれ,インデックス (index) またはマップキー (map key) と呼ばれる.
以下のルールが適用される.
a
がマップでない場合
- インデックス
x
は整数型か型なし定数型でなければならない. - 定数インデックスは非負であり,型
int
の値として表現可能でなければならない. - 型なしの定数インデックスは型
int
で与えられる. - インデックス
x
は,0 <= x < len(a)
であれば,範囲内 (in range)であり, それ以外の場合は範囲外 (out of range)である.
a
が配列型 A
の場合
- 定数インデックスは範囲内でなければならない.
- 実行時に
x
が範囲外である場合, ランタイムパニックが発生する. a[x]
はインデックスx
の配列要素であり,a[x]
の型はA
の要素型である.
a
が配列型へのポインターの場合,
a[x]
は(*a)[x]
の省略形である.
a
がスライス型 S
の場合
- 実行時に
x
が範囲外である場合, ランタイムパニックが発生する. a[x]
はインデックスx
のスライス要素であり,a[x]
の型はS
の要素型である.
a
が文字列型の場合
- 文字列
a
もまた定数の場合,定数インデックスは範囲内でなければならない. - 実行時に
x
が範囲外である場合, ランタイムパニックが発生する. a[x]
はインデックスx
の非定数バイト値であり,a[x]
の型はbyte
である.a[x]
へは代入できない場合がある.
a
がマップ型 M
の場合
x
の型はM
のキー型へ代入可能でなければならない.- マップはキー
x
のエントリーを含む場合,a[x]
はキーx
のマップ要素であり,a[x]
の型はM
の要素型である. - マップが
nil
であるか,エントリーを持たない場合,a[x]
はM
の要素型のゼロ値である.
上記以外の場合,a[x]
は不当である.
代入や初期化で使用される型 map[K]V
のマップ a
のインデックス式
v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]
は,追加の型なし真偽値を生成する.
キー x
がマップに存在するなら,
ok
の値は true
であり,
存在しないなら,false
である.
nil
要素の代入はランタイムパニックが発生する.
スライス式 (slice expression) は, 文字列,配列,配列へのポインター,または,スライスから部分文字列,または,スライスを構築する. 2 つのバリアント (variant) がある: 下限と上限を指定する単純な形式 (simple form) と, 容量の上限も指定する完全な形式 (full form) である.
文字列,配列,配列へのポインター,または,スライスに対する一次式
a[low : high]
は,部分文字列かスライスを構築する.
インデックス low
と high
はオペランド a
のどの要素が復帰値に現れるかを選択する.
復帰値は,0 から始まり,長さ high - low
のインデックスを持つ.
配列 a
をスライスすると,
a := [5]int{1, 2, 3, 4, 5}
s := a[1:4]
スライス s
は型 []int
,長さ 3,容量 4 であり,
次の要素をもつ.
s[0] == 2
s[1] == 3
s[2] == 4
利便性のため,いずれのインデックスも省略できる.
low
インデックスがない場合は 0
,
high
インデックスがない場合は,スライスされたオペランドの長さとなる.
a[2:] // a[2 : len(a)] と同じ
a[:3] // a[0 : 3] と同じ
a[:] // a[0 : len(a)] と同じ
a
が配列へのポインターである場合,
a[low : high]
は (*a)[low : high]
の省略形である.
配列か文字列の場合,
インデックスは,
0 <= low <= high <= len(a)
であれば,範囲内 (in range) であり,
そうでなければ,範囲外 (out of range) である.
スライスに対して,
インデックスの場合,
長さではなく,スライスの容量 cap(a)
である.
定数インデックスは,非負で,型 int
で表現可能でなければならない.
配列か定数文字列の場合,
定数インデックスは範囲内でなければならない.
どちらのインデックスも定数であるなら, low <= high
を満足しなければならない.
実行時にインデックスが範囲外であれば,
ランタイムパニックが発生する.
型なし文字列を除いて,
スライスされたオペランドが,文字列かスライスの場合,
スライス演算の復帰は,オペランドとして同じ型の非定数値である.
型なし文字列オペランドの場合,
復帰は,型 string
の非定数値である.
スライスオペランド (sliced operand) が配列の場合,
それはアドレス可能でなければならず,
スライス演算の復帰は,
配列として同じ要素型をもつスライスである.
有効なスライス式のスライスオペランドが nil
スライスである場合,
復帰は nil
スライスである.
そうでなければ,復帰がスライスであれば,
それは,基底配列とオペランドを共有する.
var a [10]int
s1 := a[3:7] // s1 の基底配列は配列 a; &s1[2] == &a[5]
s2 := s1[1:4] // s2 の基底配列は s1 の基底配列である配列 a; &s2[1] == &a[5]
s2[1] = 42 // s2[1] == s1[2] == a[5] == 42; すべて,同じ基底配列の要素を参照する
配列,配列へのポインター,スライス a
(文字列ではない)に対して,
一次式
a[low : high : max]
は,同じ型で単純なスライス表現 a[low : high]
と同じ長さと要素を持つスライスを構築する.
さらに,
復帰されるスライスの容量@@@
最初のインデックスのみ省略でき,その値は 0 になる.
配列 a
をスライスすると,
a := [5]int{1, 2, 3, 4, 5}
t := a[1:3:5]
スライス t
は型 []int
,長さ 2,容量 4 を持ち,要素は以下を満たす.
t[0] == 2
t[1] == 3
単純なスライス表現のように,
a
が配列へのポインターのとき,
a[low : high : max]
は,
(*a)[low : high : max]
の省略形である.
スライスオペランドが配列であれば,それは,
アドレス可能でなければならない.
インデックスは 0 <= low <= high <= max <= cap(a)
のとき,
範囲内であり,そうでなければ,範囲外である.
定数インデックスは,非負で,型 int
で表現可能でなければならない.
配列の場合,
定数インデックスは,範囲内でなければならない.
複数のインデックスが定数の場合,
定数は相対的な範囲内になければならない.
実行時にインデックスが範囲外の場合,
ランタイムパニックが発生する.
インターフェース型の式 x
と型 T
に対して,一次式
x.(T)
は,x
は nil
ではなく,x
に格納された値が型 T
であることを主張する (assert).
表記 x.(T)
は型アサーション (type assertion) と呼ばれる.
より正確には,
T
がインターフェース型でない場合,
x.(T)
は x
の動的な型が型 T
と同一であることを主張する.
このケースでは,
T
は x
の(インターフェース)型を実装しなければならない.
そうでなければ,
x
に型 T
の値を格納できないので,
型アサーションは無効となる.
T
がインターフェース型であれば,
x.(T)
は x
の動的な型がインターフェース T
を実装することを主張する.
型アサーションが成り立つとき,
式の値は,
x
に格納した値であり,
その型は T
である.
もし,型アサーションが false
であれば,
ランタイムパニックが発生する.
言い換えると,
x
の動的な型を実行時にしかわからないとしても,
正しいプログラムでは,
x.(T)
の型は T
であることがわかっている.
var x interface{} = 7 // x は動的な型 int を持ち,値 7 である
i := x.(int) // i は型 int を持ち,値 7
type I interface { m() }
func f(y I) {
s := y.(string) // 不当: string は I を実装しない (メソッド m がない)
r := y.(io.Reader) // r は型 io.Reader を持ち, y の動的な型は I と io.Reader の両方を実装しなければならない
…
}
代入や初期化で使用される型アサーション
v, ok = x.(T)
v, ok := x.(T)
var v, ok = x.(T)
var v, ok interface{} = x.(T) // v と ok の動的な型は T と bool である
は,追加の型なし真偽値を生成する.
アサーションが成り立つなら,
ok
の値は true
であり,
存在しないなら,false
であり,
v
の値は型 T
のゼロ値になる.
このケースでは,ランタイムパニックは発生しない.
関数型 F
の式 f
が与えられたとき,
f(a1, a2, … an)
は引数 a1, a2, … an
で f
を呼び出す.
特別な場合を除いて,
引数は F
のパラメーター型 (parameter type) に代入可能な単一の値をもつ式でなければならず,
関数が呼び出される前に評価される.
その式の型は,F
の復帰型 (return type) である.
メソッド呼び出しは似ているが,
メソッドはそれ自身が,
メソッドのレシーバー型の値のセレクタとして指定される.
math.Atan2(x, y) // 関数呼び出し
var pt *Point
pt.Scale(3.5) // レシーバー pt を用いたメソッド呼び出し
関数呼び出しでは, 関数値と引数は,通常の順序で評価される. それらが評価されたあと, その呼び出しのパラメーターは, 関数の値として渡され, 呼び出された関数は実行を開始する. 関数が復帰するとき, 関数の復帰パラメーターは, 呼び出し元に値が渡される.
nil
関数値の呼び出しは,
ランタイムパニックが発生する.
特別な場合として,
関数 g
またはメソッド g
の復帰値が,
他の関数 f
またはメソッド f
のパラメーターに @@@
数が等しく,個別に代入可能なとき,
呼び出し f(g(g のパラメーター))
は
f
のパラメーターに g
の復帰値を順番にバインドした後に実行される.
f
の呼び出しは g
の呼び出し以外のパラメーターを含んではならないし,
g
は少なくとも 1 つの復帰値を持たなければならない.
f
が最後に ...
パラメーターを持つ場合,
通常のパラメーターの代入後に残った g
の復帰値が代入される.
func Split(s string, pos int) (string, string) {
return s[0:pos], s[pos:]
}
func Join(s, t string) string {
return s t
}
if Join(Split(value, len(value)/2)) != value {
log.Panic("test fails")
}
x
の(型の)
メソッド集合が m
を含み,
引数リストに m
のパラメーターリストが代入可能なら,
メソッド呼び出し x.m()
は有効である.
f
がアドレス可能であり,
&x
のメソッド集合が,m
を含むとき,
x.m()
は (&x).m()
の省略形である.
var p Point
p.Scale(3.5)
別のメソッド型やメソッドリテラルはない.
f
が
型 ...T
の最後のパラメーター p
をもつ可変長引数 (variadic) な場合
f
内では,
p
の型は,型 []T
と等価である.
f
が,p
の実際の引数なしで実行された場合,
p
に渡される値は,nil
である.
そうでなければ,
その渡された値は,
連続する要素が実際の引数 (すべて T
に代入可能でなければならない) である新しい基底配列にもつ,
型 []T
の新しいスライスである.
そのため,
スライスの長さと容量は p
にバインドされる引数の数であり,
呼び出し位置毎に異なる場合がある.
次の関数と呼び出しを考えると,
func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
Greeting
内では,
最初の呼び出しでは, who
は nil
であり,
次の呼び出しでは,who
は []string{"Joe", "Anna", "Eileen"}
である.
最後の引数は,スライス型 []T
に代入可能である場合,
引数の後に ...
を続けると,
...T
パラメーターの値がそのまま渡される.
このケースでは,新しいスライスは生成されない.
次のスライス s
と呼び出しを考えると,
s := []string{"James", "Jasmine"}
Greeting("goodbye:", s...)
Greeting
内では,
s
と同じ基底配列の値をもつ.
演算子 (operator) は,オペランドを式に結合する.
Expression = UnaryExpr | Expression binary_op Expression .
UnaryExpr = PrimaryExpr | unary_op UnaryExpr .
binary_op = "||" | "&&" | rel_op | add_op | mul_op .
rel_op = "==" | "!=" | "<" | "<=" | ">" | ">=" .
add_op = " " | "-" | "|" | "^" .
mul_op = "*" | "/" | "%" | "<<" | ">>" | "&" | "&^" .
unary_op = " " | "-" | "!" | "^" | "*" | "&" | "<-" .
比較については,別の場所で説明する. 他の二項演算子の場合, 演算子がシフトか型なし定数の場合を除いて, オペランド型は同一でなければならない. 定数のみの演算の場合は,定数式節を参照されたい.
シフト演算を除いて, 一方のオペランドが型なし定数で, 他方のオペランドがそうでない場合, 定数は他方のオペランドの型に暗黙的に変換される.
シフト式における右のオペランドは,
整数型であるか,型 uint
の値で表現可能な型なし定数でなければならない.
非定数シフト式の左のオペランドが型なし定数の場合,
最初に暗黙的にシフト式が左のオペランドに置き換えられた場合に想定される型に変換される.
var a [1024]byte
var s uint = 33
// 以下の例の結果は 64bit 整数に対して与えられる
var i = 1<<s // 1 は型 int をもつ
var j int32 = 1<<s // 1 は型 int32 をもつ; j == 0
var k = uint64(1<<s) // 1 は型 uint64 をもつ; k == 1<<33
var m int = 1.0<<s // 1.0 は型 int をもつ; m == 1<<33
var n = 1.0<<s == j // 1.0 は型 int をもつ; n == true
var o = 1<<s == 2<<s // 1 と 2 は型 int をもつ; o == false
var p = 1<<s == 1<<33 // 1 は型 int をもつ; p == true
var u = 1.0<<s // 不当: 1.0 は型 float64, シフトできない
var u1 = 1.0<<s != 0 // 不当: 1.0 は型 float64, シフトできない
var u2 = 1<<s != 1.0 // 不当: 1 は型 float64, シフトできない
var v float32 = 1<<s // 不当: 1 は型 float32, シフトできない
var w int64 = 1.0<<33 // 1.0<<33 は定数シフト式; w == 1<<33
var x = a[1.0<<s] // panics: 1.0 は型 int をもつが, 1<<33 は配列境界をオーバーフローする
var b = make([]byte, 1.0<<s) // 1.0 は型 int をもつ; len(b) == 1<<33
// 以下の例の結果は 32bit 整数に対して与えられる,
// つまり,シフトがオーバーフローすることを意味する.
var mm int = 1.0<<s // 1.0 は型 int をもつ; mm == 0
var oo = 1<<s == 2<<s // 1 と 2 は型 int をもつ; oo == true
var pp = 1<<s == 1<<33 // 不当: 1 は型 int をもつが, 1<<33 は int をオーバーフローする
var xx = a[1.0<<s] // 1.0 は型 int をもつ; xx == a[0]
var bb = make([]byte, 1.0<<s) // 1.0 は型 int をもつ; len(bb) == 0
単項演算子は最も高い優先順位をもつ.
演算子と --
演算子は,
式 (expression) ではなく,文 (statement) を形成するため,
これらは,演算子の階層の外である.
結果として,文 *p
は (*p)
と同じになる.
二項演算子には 5 つの優先順位がある.
乗算演算子 mul_op
が最も強く,
加算演算子 add_op
,
比較演算子 rel_op
,
論理積 &&
,
最後に,論理和 ||
が続く:
優先順位 演算子
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 && <-chanInt > 0
算術演算子 (arithmetic operator) は,
数値に対して適用し,
最初のオペランドと同じ型の結果を生成する.
4 つの標準算術演算子 (
, -
, *
, /
) は,
整数型,浮動小数点型,複素数型に適用する.
演算子は文字列にも適用する.
ビット単位論理演算子とシフト演算子は整数のみに適用する.
和 整数, 浮動小数点, 複素数値, 文字列
- 差 整数, 浮動小数点, 複素数値
* 積 整数, 浮動小数点, 複素数値
/ 商 整数, 浮動小数点, 複素数値
% 剰余 整数
& ビット単位 AND 整数
| ビット単位 OR 整数
^ ビット単位 XOR 整数
&^ ビットクリア (AND NOT) 整数
<< 算術左シフト 整数 << 符号なし整数
>> 算術左シフト 整数 >> 符号なし整数
訳注:ビットクリアは,ビット単位 AND (論理積) のNOT (否定) を計算する.
2 つの整数値 x
と y
に対し,
整数商 (quotient) q = x / y
と剰余 (remainder) r = x % y
は,
次の関係を満足する.
x = q*y r かつ |r| < |y|
ここで,x / y
はゼロに向かって切り捨てられる
("端数処理").
x y x / y x % y
5 3 1 2
-5 3 -1 -2
5 -3 -1 2
-5 -3 1 -2
この規則の 1 つの例外は,
被除数 x
は x
の int
型における最も小さな値である (most negative value) とき,
2 の補数の整数オーバーフローのため,
商 q = x / -1
は,x
と等しく,
剰余 r = 0
である.
x, q
int8 -128
int16 -32768
int32 -2147483648
int64 -9223372036854775808
除数が定数の場合,0 であってはならない. 実行時に除数が 0 であると, ランタイムパニックが発生する. 被除数が非負で,除数が定数の 2 の冪乗である場合, 算術右シフトに置き換えられ, 剰余計算は,ビット単位論理積に置き換えられることがある.
x x / 4 x % 4 x >> 2 x & 3
11 2 3 2 3
-11 -2 -3 -3 1
シフト演算 (shift operator) は,右のオペランドで指定されるシフトカウント (shift count) によって,左のオペランドがシフトされる.
そのため,左のオペランドは非負でなければならない.
実行時にシフトカウントが負の場合,
ランタイムパニックが発生する.
シフト演算子は,
左のオペランドが符号付き整数の場合,算術シフト (arithmetic shift) を,
左のオペランドが符号なし整数の場合,論理シフト (logical shift) を実装する.
シフトカウントの上限はない.
シフトは,左のオペランドがシフトカウント n
に対して,
n
回 1 シフトされるように動作する.
結果として,x << 1
は x * 2
と同じであり,
x >> 1
は負の無限大に向かって切り捨てられるが,
x / 2
と同じである.
整数オペランドに対して,
単項演算
, -
, ^
は以下のように定義される.
x は 0 x
-x 符号反転 は 0 - x
^x ビット単位補完 は m ^ x で, m はすべてのビットに 1 をセットしたもの (符号なし x の場合)
m = -1 (符号付き x の場合)
符号なし整数に対して,
演算子
, -
, *
, <<
は,
2^n
を法として計算される.
ここで,n
は符号なし整数型のビット幅である.
大まかに言うと,
これら符号なし整数の演算は,
オーバーフロー (overflow) した上位ビットを捨て,
プログラムは,「ラップアラウンド」 (wrap arround) に依存することがある.
訳注: ラップアラウンドは,処理可能な範囲の最後に達した後に,最初に戻ること.
例えば,uint8
型の 0xff
, 0x01
に対して, 0xff 0x01
が 0x00
になる.
符号付き整数に対して,
演算子
, -
, *
, <<
は,
正当にオーバーフローし,
復帰値は存在し,
復帰値は,
符号付き整数の表現,演算子,そのオペランドによって決定的に定義される.
オーバーフローによるランタイムパニックは発生しない.
コンパイラは,
オーバーフローが発生しないことを仮定したコードの最適化は行わない場合がある.
例えば,x < x 1
はいつでも true
とは限らない.
浮動小数点数と複素数に対して,
x
は x
と同じであるが,
-x
は x
の符号反転 (negation) である.
浮動小数点か複素数をゼロで割った復帰値は
IEEE-754 標準で指定されていない.
ランタイムパニックが発生するかどうかは,実装依存である.
実装では, 複数の浮動小数点数演算を (場合によっては,文を超えて) 単一の融合演算 (fused operation) に結合し, 命令を個別に実行および丸めて得られる値と異なる結果を生成する場合がある. 明示的な浮動小数点型への変換は, 変換先の型の精度へ丸める. これは,その丸めを破棄する融合を防ぐ.
例えば,一部のアーキテクチャでは,
x * y z
の中間結果 x * y
を丸めずに計算する
「融合積和演算 (fused multiply and add; FMA)」命令を提供する.
次の例は,Go 実装がその命令を使用できるか示している:
// FMA は r の計算を許す.なぜなら, x*y は明示的な丸めがないから
r = x*y z
r = z; r = x*y
t = x*y; r = t z
*p = x*y; r = *p z
r = x*y float64(z)
// FMA は r の計算を許さない.なぜなら,x*y の丸めを省略するから
r = float64(x*y) z
r = z; r = float64(x*y)
t = float64(x*y); r = t z
文字列 (string) は,
演算子か =
代入演算子を使って連結 (concatenate) できる.
s := "hi" string(c)
s = " and good bye"
文字列の追加は, オペランドの連結によって新しい文字列が生成される.
比較演算子 (comparison operator) は, 2 つのオペランドを比較し, 型なしブール値を生成する.
== 等しい
!= 等しくない
< 小なり
<= 以下
> 大なり
>= 以上
任意の比較において, 最初のオペランドは, 2 つめのオペランドの型に, もしくはその逆に, 代入可能でなければならない.
等価演算子 (equality operator) ==
と !=
は,
比較可能 (comparable) なオペランドに適用する.
順序演算子 (ordering operator) <
, <=
, >
, >=
は,
順序付けられる (ordered) オペランドに適用する.
これらの項 (term) と比較結果は,以下のように定義される.
- ブール値は比較可能である.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
の値が比較可能で,X
がT
を実装しているときに,x
とt
は比較可能である.t
の動的な型がX
と同一であり,t
の動的な値がx
に等しいとき,x
とt
は等しい. - 構造体値は,それらのフィールドすべてが比較可能であれば,比較可能である. 2 つの構造体は,それらの非ブランクなフィールドがすべて等しければ,等しい.
- 配列の要素型の値が比較可能であれば,配列値は比較可能である. 2 つの配列値は,それらの対応する要素が等しければ,等しい.
同一の動的な型をもつ 2 つのインターフェース値の比較は, もし,その型の値が比較可能でなければ, ランタイムパニックが発生する. この動作は,直接なインターフェース値の比較だけでなく, インターフェース値の配列や, インターフェース値のフィールドをもつ構造体の比較でも適用される.
スライス値,マップ値,関数値は比較可能ではない.
しかし,特別な場合として,
スライス値,マップ値,関数値が事前宣言された識別子 nil
との比較される場合がある.
ポインター値,チャンネル値,インターフェース値の nil
との比較もまた,
許容され,上記の一般的な規則に従う.
const c = 3 < 4 // c は型なしブール定数 true
type MyBool bool
var x, y int
var (
// 比較結果は,型なしブール値である.
// 通常の代入規則が適用される.
b3 = x == y // b3 は型 bool を持つ
b4 bool = x == y // b4 は型 bool を持つ
b5 MyBool = x == y // b5 は型 MyBool を持つ
)
論理演算子 (logical operator) は, ブール値に適用され, オペランドと同じ型の復帰値を生成する. 右のオペランドは,条件付きで評価される.
&& conditional AND p && q は "もし p であれば q,そうでなければ false"
|| conditional OR p || q は "もし p であれば true,そうでなければ q"
! NOT !p は "p ではない"
型 T
のオペランド x
に対して,
アドレス演算 (address operation) &x
は,
型 *T
の x
へのポインターを生成する.
オペランドはアドレス可能 (addressable)でなければならない.
つまり,
変数,ポインター間接参照,スライス indexing 演算 @@@ であるか,
アドレス可能な構造体オペランドのフィールドセレクタであるか,
アドレス可能な配列の配列 indexing @@@ 演算であるか
でなければならない.
アドレス可能性の要件の例外として,
x
が,(丸括弧で囲まれた)複合リテラルの場合もある.
x
の評価でランタイムパニックが発生するとき,
&x
の評価でも同様にランタイムパニックが発生する.
ポインター型 *T
のオペランド x
に対して,
ポインター間接参照 *x
は
x
によってさされる型 T
の変数を示す.
x
が nil
の場合,
*x
の評価への試みは,
ランタイムパニックが発生する.
&x
&a[f(2)]
&Point{2, 3}
*p
*pf(x)
var x *int = nil
*x // ランタイムパニックが発生
&*x // ランタイムパニックが発生
チャンネル型のオペランド ch
に対して,
受信演算 (receive operation) <-ch
の値は,
チャンネル ch
から受信された値である.
チャンネルの向きは,
受信演算を許可されていなければならず,
受信演算の型は,チャンネルの要素型である.
値が利用可能になるまで,式はブロックされる.
nil
チャンネルからの受信は,永遠にブロックされる.
閉じたチャンネルでの受信演算は,いつでも,
すぐに続行し,
前に送信された値が受信された後,
要素型のゼロ値が生成される.
v1 := <-ch
v2 = <-ch
f(<-ch)
<-strobe // クロックパルスまで待機し,受信した値を破棄する
特別な形式の代入か初期化で使用される受信式は,
x, ok = <-ch
x, ok := <-ch
var x, ok = <-ch
var x, ok T = <-ch
通信が成功したかを報告する追加の型なしブール値を復帰する.
受信した値が,
チャンネルへの成功した送信演算によって配信された場合,
ok
の値は true
である.
チャンネルが閉じられたか空であるため,ゼロ値が生成された場合は,
ok
の値は false
である.
変換 (conversion) は, 式の型をその変換によって指定された型へ変更する. 変換は,ソースに文字通り現れるか, 式が現れる文脈によって,暗黙的 (implied) である場合がある.
明示的 (explicit) 変換は,
T
を型, 型 T
に変換可能な式を x
として,
形式 T(x)
である.
Conversion = Type "(" Expression [ "," ] ")" .
型が,
演算子 *
か <-
で始まっているか,
型が,
キーワード func
で始まっており,復帰値を持たない場合,
曖昧性を回避するために,必要に応じて,丸括弧で囲まなければならない:
*Point(p) // *(Point(p)) と同じ
(*Point)(p) // p は,*Point に変換される
<-chan int(c) // <-(chan int(c)) と同じ
(<-chan int)(c) // c は,<-chan int に変換される
func()(x) // 関数シグネチャー func() x
(func())(x) // x は func() に変換される
(func() int)(x) // x は func() int に変換される
func() int(x) // x は(曖昧性なしに)func() int に変換される
定数値 x
は,
x
が T
の値として表現可能であれば,
型 T
に変換される.
特別なケースとして,
整数定数 x
は,
非定数 x
のように同じルールを使用して明示的に文字列型に変換できる.
定数を変換すると,型付き定数が復帰される.
uint(iota) // 型 uint の iota 値
float32(2.718281828) // 型 float32 の 2.718281828
complex128(1) // 型 complex128 の 1.0 0.0i
float32(0.49999999) // 型 float32 の 0.5
float64(-1e-1000) // 型 float64 の 0.0
string('x') // 型 string の "x"
string(0x266c) // 型 string の "♬"
MyString("foo" "bar") // 型 MyString の "foobar"
string([]byte{'a'}) // 定数ではない: []byte{'a'} は定数ではない
(*int)(nil) // 定数ではない: nil は定数ではない, *int はブール型,数値型,文字列型ではない
int(1.2) // 不当: 1.2 は int で表現不可能
string(65.0) // 不当: 65.0 は整数定数で表現不可能
非定数値 x
は,以下のいずれの場合でも型 T
に変換できる
- x は
T
に代入可能である. - 構造体タグを無視し(下記参照),
x
の型とT
は同一の基底型をもつ. - 構造体タグを無視し(下記参照),
x
の型とT
は定義型ではないポインター型であり, それらのポインターの基礎型が,同一の基底型をもつ. x
の型とT
はどちらも整数型か浮動小数点型である.x
の型とT
はどちらも複素数型である.x
は整数であるか,バイトかルーンのスライスであり,T
は文字列型である.x
は文字列であり,T
はバイトかルーンのスライスである.
構造体タグは, 変換の目的で同一の構造体型を比較するとき,無視される.
type Person struct {
Name string
Address *struct {
Street string
City string
}
}
var data *struct {
Name string `json:"name"`
Address *struct {
Street string `json:"street"`
City string `json:"city"`
} `json:"address"`
}
var person = (*Person)(data) // タグは無視する.基底型は同一である
特別な規則が,
数値型間の(非定数)変換,
文字列型へ,または文字列型からの(非定数)変換に適用される.
これらの変換は,
x
の表現を変更し,
実行時のコストが発生する場合がある.
すべての他の変換は,単に型を変更するだけであり,
x
の表現は変更されない.
ポインターと整数の間で変換する言語メカニズムはない. パッケージ unsafeは制限された状況下で,この機能を実装する.
非定数数値の変換では,以下の規則が適用される.
- 整数型間で変換するとき,
その値が符号付き整数であるなら,
暗黙的に無限大精度に拡張された符号である.
それ以外の場合は,拡張されたゼロである.
復帰型のサイズにあうように切り捨てられる.
例えば,
v := uint16(0x10F0)
であれば,uint32(int8(v)) == 0xFFFFFFF0
となる. 変換はいつでも,有効な値を生成し, オーバーフローの兆候 (indication) はない. - 浮動小数点数から整数への変換では, 小数部は破棄される (ゼロに向かって切り捨て).
- 整数か浮動小数点から,浮動小数点への変換または,
複素数値から,他の複素型へ変換するとき,
復帰値は,変換先の型で指定された精度に丸められる.
例えば,
float32
型の変数の値は, IEEE-754 32ビット数の精度を超える追加の精度を使用して格納されるが,float32(x)
はx
の丸められた値は, 32ビット精度で表現する. 同様に,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"
- バイトのスライスを文字列へ変換すると, 連続したバイトをスライスの要素とする文字列を生成される.
string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø"
string([]byte{}) // ""
string([]byte(nil)) // ""
type MyBytes []byte
string(MyBytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø"
- ルーンのスライスを文字列型へ変換すると, 個々のルーン値を連結した文字列が生成される.
string([]rune{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔"
string([]rune{}) // ""
string([]rune(nil)) // ""
type MyRunes []rune
string(MyRunes{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔"
- 文字列型の値をバイト型のスライスへ変換すると, 連続した要素がその文字列のバイトたちであるスライスが生成される.
[]byte("hellø") // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
[]byte("") // []byte{}
MyBytes("hellø") // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
- 文字列型の値をルーン型のスライスへ変換すると, 文字列の個々の Unicode 符号位置を含むスライスが生成される.
[]rune(MyString("白鵬翔")) // []rune{0x767d, 0x9d6c, 0x7fd4}
[]rune("") // []rune{}
MyRunes("白鵬翔") // []rune{0x767d, 0x9d6c, 0x7fd4}
定数式 (constant expression) は, 定数オペランドのみを含み, コンパイル時に評価される.
型なしブール定数, 型なし数値定数, 型なし文字列定数は, それぞれ, ブール型,数値型,文字列型のオペランドの使用が正当な場合には, オペランドとして使用できる.
定数の比較はいつでも,型なしブール定数を生成する. 定数シフト式の左のオペランドが,型なし定数の場合, 復帰値は整数定数である. そうでなければ,左のオペランドと同じ型の定数であり, それは,整数型でなければならない.
型なし定数における他のオペランドは, 同じ種類の型なし定数を復帰する. つまり, ブール定数,整数定数,浮動小数点定数,複素数定数,文字列定数である. 二項演算(シフト演算を除く)の型なしオペランドの種類が,異なる場合, 復帰値は, 整数,ルーン,浮動小数点,複素数の後半のオペランドの種別になる.@@@ 例えば,型なし整数定数は, 型なし複素数定数で割ると, 型なし複素数定数が生成される.
const a = 2 3.0 // a == 5.0 (型なし浮動小数点定数)
const b = 15 / 4 // b == 3 (型なし整数定数)
const c = 15 / 4.0 // c == 3.75 (型なし浮動小数点定数)
const Θ float64 = 3/2 // Θ == 1.0 (型 float64, 3/2 は整数除算)
const Π float64 = 3/2. // Π == 1.5 (型 float64, 3/2. は浮動小数除算)
const d = 1 << 3.0 // d == 8 (型なし整数定数)
const e = 1.0 << 3 // e == 8 (型なし整数定数)
const f = int32(1) << 33 // 不当 (定数 8589934592 は int32 でオーバーフローする)
const g = float64(2) >> 1 // 不当 (float64(2) は型付き浮動小数点定数)
const h = "foo" > "bar" // h == true (型なしブール定数)
const j = true // j == true (型なしブール定数)
const k = 'w' 1 // k == 'x' (型なしルーン定数)
const l = "hi" // l == "hi" (型なし文字列定数)
const m = string(k) // m == "x" (型 string)
const Σ = 1 - 0.707i // (型なし複素数定数)
const Δ = Σ 2.0e-4 // (型なし複素数定数)
const Φ = iota*1i - 1/1i // (型なし複素数定数)
ビルトイン関数 complex
を,
型なし整数定数,
型なしルーン定数,
型なし浮動小数点定数に適用すると,
型なし浮動小数点定数が生成される.
const ic = complex(0, c) // ic == 3.75i (型なし複素数定数)
const iΘ = complex(0, Θ) // iΘ == 1i (型 complex128)
定数式はいつも正確に評価される; 中間値と定数自身は, 言語が事前宣言した型によってサポートされる精度より遥かに大きな精度を要求する場合もある. 以下は,正当な宣言である.
const Huge = 1 << 100 // Huge == 1267650600228229401496703205376 (型なし整数定数)
const Four int8 = Huge >> 98 // Four == 4 (型 int8)
定数除算や剰余演算の除数はゼロであってはならない:
3.14 / 0.0 // 不当:0 による除算
型付き定数の値は, 定数型の値によって正確に表現可能でなければならない. 以下の定数表現は,正当である:
uint(-1) // -1 は uint で表現できない
int(3.14) // 3.14 は int で表現できない
int64(Huge) // 1267650600228229401496703205376 は int64 で表現できない
Four * 300 // オペランド 300 は (Four の型である) int8 で表現できない
Four * 100 // 積 400 は (Four の型である) int8 で表現できない
単項ビット単位補完 (bitwise complement) 演算子 ^
は,
非定数に対する規則に一致する.
マスクは,
符号なしの場合はすべて 1 の定数と,
符号付き型なし定数の場合は -1 である.
^1 // 型なし整数定数で, -2 と等しい
uint8(^1) // 不当: uint8(-2) と同じ,-2 は uint8 で表現できない
^uint8(1) // 型 uint8 定数, 0xFF ^ uint8(1) = uint8(0xFE) と同じ
int8(^1) // int8(-2) と同じ
^int8(1) // -1 ^ int8(1) = -2 と同じ
実装上の制限: コンパイラは, 型なし浮動小数点定数式,または,複素数定数式を計算するときに, 丸めを使用することがある. 定数節における実装上の制限を参照せよ. 無限の精度を使用して計算して整数になったとしても, この丸めは,浮動小数点定数式が, 整数のコンテキストでは無効になる場合がある.
パッケージレベルでは,
初期化の依存関係 (initialization dependency) によって,
変数宣言の個々の初期化式の評価順序 (evaluation order) が決まる.
それ以外の場合には,
式,代入,return
文の評価のとき,
すべての関数呼び出し,メソッド呼び出し,通信演算は,
字句的に左から右の順序で評価される.
例えば,(関数ローカルな)代入では,
y[f()], ok = g(h(), i() x[j()], <-c), k()
関数呼び出しと,通信は,
f(x)
, h()
, i()
, j()
, <-c
, g()
, k()
の順序で発生する.
しかし,
これらのイベントの評価と,
x
のインデックスの作成,
y
の評価の順序は,指定されていない.
a := 1
f := func() int { a ; return a }
x := []int{a, f()} // x は [1, 2] か [2, 2] である: a と f() の評価順序は指定されていない
m := map[int]int{a: 1, a: 2} // m は {2: 1} か {2: 2} である: 2 つのマップの代入間の順序は指定されていない
n := map[int]int{a: f()} // n は {2: 3} か {3: 3} である: キーと値の評価順序は指定されていない
パッケージレベルでは, 初期化の依存関係は,個々の初期化式に対する左から右へのルールで上書きされるが, どちらの式にも含まれるオペランドに対しては,上書きされない.
var a, b, c = f() v(), g(), sqr(u()) v()
func f() int { return c }
func g() int { return a }
func sqr(x int) int { return x*x }
// 関数 u と v はすべての他の変数と関数から独立している
関数呼び出しは,
u()
, sqr()
, v()
, f()
, v()
, g()
の順序で発生する.
単一の式に含まれる浮動小数点演算は,
演算子の結合性に従って評価される.
明示的な丸括弧は,
デフォルトの結合性を上書きして評価に影響を与える.
式 x (y z)
は,x
の加算の前に加算 y z
が実行される.
文 (statement) は実行を制御する.
Statement =
Declaration | LabeledStmt | SimpleStmt |
GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
DeferStmt .
SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .
終端文 (terminating statement) 同一ブロック内で,その後に字句的に現れるすべての文の実行を防ぐ. 次の文は終端である.
else
節があり,かつ- どちらの節も終端文である.
for
文で,
- その
for
文を参照するbreak
文がない,かつ - ループ条件が省略されている.
switch
文であり,
- その
switch
文を参照するbreak
文がない,かつ, default
ケースがある,かつ,default
を含むいずれの文のリストも,終端文で終わるか, ラベル付けされたfallthrough
文である.
select
文であり,
- その
select
文を参照するbreak
文がない,かつ, default
を含むいずれの文リストも,終端文で終わる.
- 終端文をラベル付けするラベル文
他のすべての文は終端ではない.
リストが空でなく,その最後の非空文が終端である場合, 文リストは,終端文で終了する.
空文 (empty statement) は何もしない.
EmptyStmt = .
ラベル文 (labeled statement)は,
goto
文,break
文,continue
文のターゲットになる.
特定のビルトイン関数を除いて, 関数呼び出しとメソッド呼び出しと受信演算は, 文コンテキストに現れうる. そのような文は,丸括弧で囲まれている場合がある.
ExpressionStmt = Expression .
以下のビルトイン関数は,文コンテキスト内では使用できない.
append cap complex imag len make new real
unsafe.Alignof unsafe.Offsetof unsafe.Sizeof
h(x y)
f.Close()
<-ch
(<-ch)
len("foo") // len がビルトイン関数である場合は不当
送信文 (send statement) はチャンネルで,値を送信する. チャンネル式は,チャンネル型であり, チャンネルの方向は送信演算を許可し, 送信される値の型は,チャンネルの要素型に代入可能でなければならない.
SendStmt = Channel "<-" Expression .
Channel = Expression .
チャンネルと値式の両方が通信の開始前に評価される.
送信が続行できるまで,通信はブロックされる.
バッファリングされていないチャンネルの送信は,
受信者の準備ができている場合に,続行される.
バッファリングされているチャンネルの送信は,
バッファに開きがある場合,続行される.
閉じられたチャンネルへの送信は,
ランタイムパニックが発生する.
nil
チャンネルへの送信は永久にブロックされる.
ch <- 3 // 値 3 をチャンネル ch に送信
文と --
文は,
型なし定数 1 によってそれらのオペランドをインクリメント (increment),または,デクリメント (decrement)する.
代入と同様に,
オペランドは,アドレス可能であるか,
マップのインデックス式でなければならない.
IncDecStmt = Expression ( " " | "--" ) .
次の代入文は,意味的には等価である:
IncDec statement Assignment
x x = 1
x-- x -= 1
Assignment = ExpressionList assign_op ExpressionList .
assign_op = [ add_op | mul_op ] "=" .
左側のオペランドはアドレス可能であるか,
マップのインデックス式であるか,
(=
による代入に限り)ブランク識別子でなければならない.
オペランドは,丸括弧で囲むことができる.
x = 1
*p = f()
a[i] = 23
(k) = <-ch // k = <-ch と同じ
代入演算 (assignment operation) x op= y
は,
x = x op (y)
と等価であるが, x
は一度しか評価されない.
ここで op
は二項算術演算子とする.
op=
は単一のトークンである.
代入演算において,左側の式リストも右側の式リストもどちらも
ちょうどひとつの単一の値をもつ式でなければならず,
左側の式は,ブランク識別子であってはならない.
a[i] <<= 2
i &^= 1<<n
代入の組は,
多値演算の個々の要素を変数のリストに代入する.
2 つの形式がある.
1 つめの形式は,右辺のオペランドが
関数呼び出し,チャンネル演算,マップ演算,
型アサーションのような
単一の多値をもつ式である.
左辺のオペランドの数は,右辺の値の数と一致しなければならない.
例えば,f
が 2 値を復帰する関数の場合,
x, y = f()
は,最初の値は x
に, 2 番目の値は y
に代入される.
2 つめの形式では,
左辺のオペランドの数が,
右辺の式の数が等しく,
すべての式は単一の値を復帰しなければならず,
右辺の n
番目の式は,
左辺の n
番目のオペランドに代入される.
one, two, three = '一', '二', '三'
ブランク識別子は代入における右辺の値を無視する方法を提供する:
_ = x // x は評価するが,その値は無視する
x, _ = f() // f() は評価するが,2 番目の復帰値は無視する
代入は 2 つのフェーズで進行する. 最初に, 左辺のインデックス式のオペランドと 左辺のポインター間接参照 (セレクタにおける暗黙的なポインター間接参照を含む)と 右辺の式はすべて, 通常の順序で評価される. 2 つめに, 代入は,左から右への順序で実行される.
a, b = b, a // a と b を交換する
x := []int{1, 2, 3}
i := 0
i, x[i] = 1, 2 // i = 1, x[0] = 2 を設定する
i = 0
x[i], i = 2, 1 // x[0] = 2, i = 1 を設定する
x[0], x[0] = 1, 2 // x[0] = 1 を設定した後,x[0] = 2 を設定する (最終的に x[0] == 2)
x[1], x[3] = 4, 5 // x[1] = 4 を設定した後, x[3] = 5 でパニックが起きる
type Point struct { x, y int }
var p *Point
x[2], p.x = 6, 7 // x[2] = 6 を設定し,p.x = 7 でパニックが起きる
i = 2
x = []int{3, 5, 7}
for i, x[i] = range x { // i, x[2] = 0, x[0] を設定する
break
}
// このループの後, i == 0 と x == []int{3, 5, 3} である
代入では,すべての値が 代入されるオペランドの型に代入可能でなければならず, 次の特殊なケースがある:
- すべての型付き値は,ブランク識別子に代入できる
- 型なし定数がインターフェース型の変数かブランク識別子に代入される場合, 定数は最初に暗黙的にそのデフォルト型に変換される.
- 型なしブール値がインターフェース型の変数かブランク識別子に代入される場合,
最初に暗黙的に型
bool
に変換される.
if
文は,
ブール式 Expression
の値に従って,
2 つの分岐の条件付き実行を指定する.
ブール式が true
と評価された場合,
if
節が実行される.
そうでない場合で, もしあれば,else
節が実行される.
IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .
if x > max {
x = max
}
ブール式が評価されるまでに実行される
単順な式 SimpleStmt
をブール式の前に置くことができる.
if x := f(); x < y {
return x
} else if x > z {
return z
} else {
return y
}
switch
文は,
多方向の実行を提供する.
式か型指定子は,
switch
内の
ケース (case) たちと比較され,実行する節が決定する.
SwitchStmt = ExprSwitchStmt | TypeSwitchStmt .
式スイッチ (expression switch, ExprSwitchStmt
) と,型スイッチ (type switch, TypeSwitchStmt
) の 2 つの形式がある.
式スイッチでは,
case
は,
スイッチ式 (Expression
) の値と比較可能な式を含む.
型スイッチでは,
case
は,
特別に注釈がつけられたスイッチ式の型と比較可能な型を含む.
スイッチ式は,スイッチ文内で,一度だけ評価される.
式スイッチでは,
スイッチ式 (Expression
) は評価され,
ケース式 (定数である必要はない)
は,左から右へ,そして,上から下へ評価される.
スイッチ式と等価な最初のものが,
関連するケースの文の実行のトリガーとなる.
それ以外のケースはスキップされる.
どのケースもマッチせず,
default
ケースが存在する場合には,
default
ケースの文が実行される.
多くとも一つの default
ケースが存在でき,
switch
文内のどこに現れても良い.
スイッチ式 (Expression
) を省略した場合は,
ブール値 true
を指定された場合と等価である.
ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
ExprCaseClause = ExprSwitchCase ":" StatementList .
ExprSwitchCase = "case" ExpressionList | "default" .
スイッチ式が型なし定数と評価された場合,
その定数は最初に暗黙的にそのデフォルト型に変換される.
もし,それが型なしブール値なら,最初に型 bool
に暗黙的に変換される.
事前宣言された型なし値 nil
は
スイッチ式として使用できない.
ケース式が型なしなら,
最初にスイッチ式の型に暗黙的に変換される.
各(変換された)ケース式 x
とスイッチ式の値 t
に対して,
x == t
は有効な比較でなければならない.
言い換えると,
スイッチ式は,明示的な型なしで一時的に変数 t
として宣言,初期化されたように扱われ,
各ケース式 x
が t
の値と等しいかどうかテストされる.
ケース句か default
句では,
最後の空でない文が,
制御がこの句の最後から次の句の最初の文へ流れることを示す
(ラベル付き) fallthrough
文
の場合がある.
そうでなければ,制御は switch
文の最後に行く.
fallthrough
文は,
式スイッチの最後の句を除いて,すべての最後の文に表示できる.
スイッチ式が評価されるまでに実行される
単順な式 SimpleStmt
をブール式の前に置くことができる.
switch tag {
default: s3()
case 0, 1, 2, 3: s1()
case 4, 5, 6, 7: s2()
}
switch x := f(); { // スイッチ式を省略すると "true" を意味する
case x < 0: return -x
default: return x
}
switch {
case x < y: f1()
case x < z: f2()
case x == 4: f3()
}
実装上の制限: コンパイラは, 同じ定数に評価される 複数のケース式を許可しない場合がある. 例えば, 現在のコンパイラでは, ケース式で 整数定数,浮動小数点定数,文字列定数の重複を許可していない.
型スイッチ (type switch) は,
値ではなく,型を比較する.
それ以外は式スイッチと同様である.
実際の型ではなく,予約後 type
を使って,
型アサーションの形式を持つ
特別なスイッチ式によってマークされる.
switch x.(type) {
// cases
}
次に,ケースは
実際の型 T
を
式 x
の動的な型と
照合する.
型アサーションと同様に,
x
はインターフェース型でなければならないし,
ケースにリストされる
各非インターフェース型 T
が
x
の型を実装しなければならない.
型スイッチのケースたちにリストされる型はすべて異ならなければならない.
TypeSwitchStmt = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" .
TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
TypeCaseClause = TypeSwitchCase ":" StatementList .
TypeSwitchCase = "case" TypeList | "default" .
TypeList = Type { "," Type } .
TypeSwitchGuard
は簡潔な変数宣言 SimpleStmt
を含めることができる.
その形式が使用されると,
その変数は
各句の暗黙的なブロックの
TypeSwitchCase
の最後で宣言される.
TypeList
に一つの型のみを並べるケース句では,
変数はその型をもつ.
そうでなければ,
変数は,TypeSwitchGuard
における式の型をもつ.
型の代わりに,
事前宣言された識別子 nil
が使用されることがある;
その場合には,TypeSwitchGuard
における式が
nil
インターフェース値 @@@ である場合に選択される.
多くとも一度だけ nil
ケースが許される.
型 interface{}
の式 x
が与えられたとき,
以下の型スイッチを考えると,
switch i := x.(type) {
case nil:
printString("x is nil") // i の型は x の型である (interface{})
case int:
printInt(i) // i の型は int の型である
case float64:
printFloat64(i) // i の型は float64 の型である
case func(int) float64:
printFunction(i) // i の型は func(int) float64 の型である
case bool, string:
printString("type is bool or string") // i の型は x の型 (interface{}) である
default:
printString("don't know the type") // i の型は x の型 (interface{}) である
}
は,下のように書き換えられる.
v := x // x is evaluated exactly once
if v == nil {
i := v // type of i is type of x (interface{})
printString("x is nil")
} else if i, isInt := v.(int); isInt {
printInt(i) // type of i is int
} else if i, isFloat64 := v.(float64); isFloat64 {
printFloat64(i) // type of i is float64
} else if i, isFunc := v.(func(int) float64); isFunc {
printFunction(i) // type of i is func(int) float64
} else {
_, isBool := v.(bool)
_, isString := v.(string)
if isBool || isString {
i := v // type of i is type of x (interface{})
printString("type is bool or string")
} else {
i := v // type of i is type of x (interface{})
printString("don't know the type")
}
}
型スイッチガード TypeSwitchGuard
は
ガードが評価されるまでに実行される
単順な式 SimpleStmt
をブール式の前に置くことができる.
fallthrough
は型スイッチでは許可されていない.
for
文はブロックの繰り返し実行を指定する.
反復 (iteration) は,
単一の条件で指定,
for
句で指定,
range
句で指定で制御する
3 つの形式がある.
ForStmt = "for" [ Condition | ForClause | RangeClause ] Block .
Condition = Expression .
最も単純な形式では,
for
文は,
ブール条件 Condition
が true
と評価される限り
ブロックの繰り返し実行を指定する.
ブール条件は,各反復の前に評価される.
ブール条件が省略された場合は,
true
が指定された場合と等価である.
for a < b {
a *= 2
}
ForClause
を用いた for
文もまた,
その条件によって制御されるが,
追加で,
代入のような
初期化文 (init statement; InitStmt
) と,
インクリメント文やデクリメント文のような
更新文 (post statement; PostStmt
@@@反復文・後処理文... a tour of Go 日本語訳では,後処理文) が指定される.
初期化文は,簡潔な変数宣言の場合があるが,
更新文はそうではない.
初期化文における
変数宣言は,各反復で再利用される.
ForClause = [ InitStmt ] ";" [ Condition ] ";" [ PostStmt ] .
InitStmt = SimpleStmt .
PostStmt = SimpleStmt .
for i := 0; i < 10; i {
f(i)
}
空でない場合,
初期化文が最初の反復の条件の評価の前に一度だけ実行される.
更新文は,
(ブロックが実行された場合のみ)
そのブロックの各反復の後に実行される.
ForClause
の任意の要素は,空でも良いが,
セミコロンは,条件しかない場合を除いて必須である.
条件が省略された場合は,
true
が指定された場合と等価である.
for cond { S() } は次と同じである for ; cond ; { S() }
for { S() } は次と同じである for true { S() }
range
句を用いた for
文は,
配列,スライス,文字列,マップ,チャンネルが受信した値のすべての
エントリーを反復する.
各エントリーでは,もしあれば,
反復値 (iteration value) は,
反復変数 (iteration variable) に代入され,
ブロックが実行される.
RangeClause = [ ExpressionList "=" | IdentifierList ":=" ] "range" Expression .
range
句の右側の式は,
範囲式 (range expression) と呼ばれる.
これは,配列,配列へのポインター,スライス,文字列,マップ,
受信演算が許可されたチャンネルの場合がある.
代入の場合と同様に,指定された場合には,
反復変数を表す
左側にオペランドは,
アドレス可能であるか
map index 式でなければならない.
範囲式がチャンネルの場合,
多くともひとつの反復変数が許され,
そうでなければ,最大 2 つまでである.
最後の反復変数が,ブランク識別子の場合,
range
句は,識別子が指定されなかった場合と等価である.
範囲式 x
は,ひとつの例外を除いて,
ループの開始前に一度だけ評価される.
多くともひとつの反復変数が指定され,
len(x)
が定数の場合,
範囲式は評価されない.
左側の関数呼び出しは反復毎に一度だけ評価される. 各反復では,反復変数は,対応する反復変数が存在する場合, 以下のように生成される.
範囲式 1つ目の値 2つ目の値
array or slice a [n]E, *[n]E, or []E インデックス i int a[i] E
string s string type インデックス i int 以下参照 rune
map m map[K]V キー k K m[k] V
channel c chan E, <-chan E 要素 e E
- 配列,配列へのポインター,またはスライス値の
a
に対して, インデックス反復値は,0
から始まる要素インデックスが昇順で生成される. 多くともひとつの反復変数が指定された場合, 範囲ループは,0
からlen(a) - 1
までの反復値が生成され, 配列やスライス自身インデックスされない.nil
スライスに対しては,反復数は 0 である. - 文字列値に対して,
range
句は,0 から始まるバイトインデックスにおける Unicode 符号位置を反復する. 連続する反復では, 「1つ目の値」インデックス値は, 文字列における UTF-8 エンコードされた符号位置の最初のバイトのインデックスであり, 型rune
である「2つ目の値」は対応する符号位置の値になる. 反復が,無効な UTF-8 列を検出すると, 「2つ目の値」は0xFFFD
(Unicode 置換文字) になり, 次の反復は,文字列の 1 バイトに進む. - マップでの反復順序は,指定されないし,
次の反復が前の反復とで反復順序が同じことは保証されない.
マップのエントリーが,反復の途中で,
そのエントリーに達する前に削除された場合,
対応する反復値は生成されない.
新しいマップのエントリーが反復中に生成されると,
そのエントリーは,反復中に生成されるかもしれないし,飛ばされるかもしれない.
エントリーが生成されるかの選択は,
各生成されるエントリーに毎に,また,反復毎に異なるかもしれない.
マップが
nil
の場合,反復数は 0 である. - チャンネルに対して,
生成される反復値は
チャンネルが閉じられるまでチャンネルに送信される連続値である.
チャンネル
nil
の場合,範囲式は永久にブロックされる.
反復値は,代入文における反復変数に代入される.
反復変数は,
簡潔な変数宣言 (:=
) の形式を使って,
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
for i, s := range a {
// i の型は int
// s の型は string
// s == a[i]
g(i, s)
}
var key string
var val interface{} // m の要素型は val に代入可能
m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6}
for key, val = range m {
h(key, val)
}
// key == 反復での最後の map キー
// val == map[key]
var ch chan Work = producer()
for w := range ch {
doWork(w)
}
// 空のチャンネル
for range ch {}
go
文は
同じアドレス空間内で,
制御の独立した並行スレッドか,ゴルーチン (goroutine) として関数呼び出しの実行を開始する.
GoStmt = "go" Expression .
式 Expression
は,関数呼び出しかメソッド呼び出しでなければならず,
丸括弧で囲うことはできない.
ビルトイン関数の呼び出しは,式文と同様に制限されている.
関数値とパラメーターは,呼び出し側のゴルーチンの内で,通常通り評価されるが, 通常の呼び出しとは異なり, プログラムの実行は実行された関数の完了を待たない. 代わりに,関数は新しいゴルーチン内で独立に実行を開始する. 関数が停止したら,そのゴルーチンもまた停止する. 関数が何かしらの復帰値を持っていたら, それらは関数が完了すると破棄される.
go Server()
go func(ch chan<- bool) { for { sleep(10); ch <- true }} (c)
select
文は,
可能な送信演算か受信演算の
どちらを続行するかを選択する.
これは,switch
文に似ているが,
すべての case
は通信演算 (communication operation) を参照している.
SelectStmt = "select" "{" { CommClause } "}" .
CommClause = CommCase ":" StatementList .
CommCase = "case" ( SendStmt | RecvStmt ) | "default" .
RecvStmt = [ ExpressionList "=" | IdentifierList ":=" ] RecvExpr .
RecvExpr = Expression .
RecvStmt
を伴う case
は,
RecvExpr
の復帰を
1 つまたは 2 つの変数に代入する.
これらの変数は,
簡潔な変数宣言が使用される場合がある.
RecvExpr
は,(丸括弧で囲まれた)受信演算でなければならない.
多くともひとつの default case
が存在し,
case
文のリストのどこに書いても良い.
select
文の実行は以下のステップで進行する.
select
文におけるすべてのcase
は, 受信演算のチャンネルオペランドと, 送信文の右辺の式は,select
文に入ったときに, ソース順で, 一度だけ評価される. 復帰値は, 受信または送信するチャンネルの集合と, 送信する対応した値である. その評価での副作用は, どの通信演算 (存在する場合) を選択して続行するかに関係なく発生する. 簡潔な変数宣言か代入を伴うRecvStmt
の左辺の式はまだ評価されない.- 1 つ以上の通信が続行できる場合,
続行可能な単一のものが,一様擬似乱数により選択される.
そうでなければ,もし,
default
があれば,それが選択される. もし,default
がなければ,select
文は少なくとも 1 つの通信が続行できるようになるまで, ブロックする. - 選択された
case
がdefault
でない場合, 対応する通信演算が実行される. - 選択された
case
が簡潔な変数宣言か代入を伴うRecvStmt
の場合, 左辺の式は評価され,受信値が代入される. - 選択された
case
の文リストは実行される.
nil
チャンネルの通信は継続されないので,
nil
チャンネルを伴う select
が default
をもたないとき,
永久にブロックされる.
var a []int
var c, c1, c2, c3, c4 chan int
var i1, i2 int
select {
case i1 = <-c1:
print("received ", i1, " from c1\n")
case c2 <- i2:
print("sent ", i2, " to c2\n")
case i3, ok := (<-c3): // 次と同じ: i3, ok := <-c3
if ok {
print("received ", i3, " from c3\n")
} else {
print("c3 is closed\n")
}
case a[f()] = <-c4:
// 次と同じ:
// case t := <-c4
// a[f()] = t
default:
print("no communication\n")
}
for { // c にビットのランダム列を送信する
select {
case c <- 0: // 注意: 文なし, fallthrough なし, no folding of cases@@@
case c <- 1:
}
}
select {} // 永久にブロックされる
関数 F
における return
文は,
F
の実行を停止し,
オプションで 1 個以上の値を復帰する.
F
によって遅延されるすべての関数が,
F
がその呼び出し元に復帰する前に実行される.
ReturnStmt = "return" [ ExpressionList ] .
復帰型を持たない関数では,
return
文で復帰値を指定してはならない.
func noResult() {
return
}
復帰型をもつ関数からの値を復帰する方法は 3 つある.
- 復帰値は,明示的に
return
文でリストされる. 各式は,単一の値をもち, 関数の復帰型の対応する要素に代入可能でなければならない.
func simpleF() int {
return 2
}
func complexF1() (re float64, im float64) {
return -7.0, -4.0
}
return
文の式リスト (ExpressionList
) は,多値を復帰する関数の単一の呼び出しである. その結果, その関数から復帰された値が対応する値の型をもつ一時変数に代入され, その後, (前の方法のように)その一時変数をリストするreturn
文が続くようになる.
func complexF2() (re float64, im float64) {
return complexF1()
}
- 関数の復帰型が復帰パラメーターの名前が指定されているとき,
式リスト (
ExpressionList
) は空である. 復帰パラメーターは,通常のローカル変数として機能し, 必要に応じて代入される.return
文は,これらの変数の値を復帰する.
func complexF3() (re float64, im float64) {
re = 7.0
im = 4.0
return
}
func (devnull) Write(p []byte) (n int, _ error) {
n = len(p)
return
}
これらがどのように宣言されているかに関係なく,
すべての復帰値は,その関数に入るときに,その型のゼロ値で初期化される.
すべての遅延された関数が実行される前に,
結果を指定する return
文が復帰パラメーターを設定する.
実装上の制限:
コンパイラは,
復帰パラメーターと同じ名前をもつ
異なるエンティティ (定数,型,変数)が
return
場所と同じスコープにあるとき,
空の式リストをもつ return
文を許可しない場合がある.
func f(n int) (res int, err error) {
if _, err := f(n-1); err != nil {
return // 無効な return 文: err は隠される
}
return
}
break
文は,
同じ関数内で,
最も内側の for
文,switch
文,select
文の実行を停止する.
BreakStmt = "break" [ Label ] .
ラベルがある場合は,
そのラベルは, for
文,switch
文,select
文に対するラベルでなければならず,
その実行が停止される.
OuterLoop:
for i = 0; i < n; i {
for j = 0; j < m; j {
switch a[i][j] {
case nil:
state = Error
break OuterLoop
case item:
state = Found
break OuterLoop
}
}
}
continue
文は,
最も内側のfor
ループの
更新文 (post statement) で次の反復を開始する.
for
ループは,同じ関数内でなければならない.
ContinueStmt = "continue" [ Label ] .
ラベルがある場合には,
ラベルは,for
文に対するもので,
その実行が進む.
RowLoop:
for y, row := range rows {
for x, data := range row {
if data == endOfRow {
continue RowLoop
}
row[x] = data bias(x, y)
}
}
goto
文は,
同じ関数内の対応するラベルへ文の制御を転送する.
GotoStmt = "goto" Label .
goto Error
goto
文を実行するとき,
goto
時点ではまだスコープ内になかった変数がスコープ内に入ってはならない.
例えば,
goto L // BAD
v := 3
L:
では,ラベル L
への飛ぶと,v
の生成がスキップされるため,エラーになる.
ブロックの外への goto
文は,
そのブロックの中のラベルへ飛べない.
例えば,
if n%2 == 1 {
goto L1
}
for n > 0 {
f()
n--
L1:
f()
n--
}
は,ラベル L1
は for
文のブロック内にあるが, goto
はそうではないため,エラーになる.
fallthrough
文は
switch
文における,次の case
句の最初の文に制御を転送する.
それは,そのような句の最後の空でない文としてのみ使用できる.
FallthroughStmt = "fallthrough" .
defer
文は,
return
文に達するか,
関数本体の最後に達するか
対応するゴルーチンがパニックで落ちることによって
defer
文を囲む親関数 (surrounding function) が
復帰するときまで実行が遅延された (deferred) 関数を呼び出す.
DeferStmt = "defer" Expression .
式 Expression
は(丸括弧で囲まれていない)関数呼び出しかメソッド呼び出しでなければならない.
ビルトイン関数の呼び出しは,式文と同様に制限されている.
defer
文が実行されるたびに,
その呼び出しでの
関数値とパラメーターは,通常通り評価され,
新たに保存される (saved anew) が,
実際の関数は呼び出されない.
代わりに,遅延された関数は,
親関数が復帰する直前に
関数が遅延される順番と逆順に
呼び出される.
つまり,親関数が
明示的な return
文で復帰する場合,
すべての復帰パラメーターが,その復帰文で設定された後で,
関数が呼び出し元に戻る前に
遅延関数は実行される.
遅延関数の値が nil
の場合,
defer
文が実行されるときではなく,
その遅延関数が呼び出されるときに,
実行はパニックする.
例えば, 遅延関数が関数リテラルであり, 親関数が リテラルのスコープに属する名前付き復帰パラメーターを持つとき, 遅延関数は,それらが復帰する前に関数パラメーターはアクセスされ,変更される. 遅延関数が復帰値をもつとき, 関数が完了すると破棄される. (パニックの対処節を参照)
lock(l)
defer unlock(l) // 親関数が復帰する前に unlock する
// 親関数が復帰する前に 3 2 1 0 を表示する
for i := 0; i <= 3; i {
defer fmt.Print(i)
}
// f は 42 を復帰する
func f() (result int) {
defer func() {
// result は return 文で 6 が設定されたあと,アクセスされる
result *= 7
}()
return 6
}
ビルトイン関数(組み込み関数; built-in function) は事前宣言されている. ビルトイン関数は, 他の関数のように呼び出されるが, 一部は,最初の引数として式の代わりに型を受け入れる.
ビルトイン関数は,標準的な go 型を持たないため, ビルトイン関数は呼び出し式においてのみ現れる. ビルトイン関数は関数値として使用できない.
チャンネル c
に対して,
ビルトイン関数 close(c)
は
チャンネルに送信する値がないことを記録する.
c
が受信専用チャンネルの場合,エラーになる.
閉じたチャンネルに対して送信するか,閉じるとランタイムパニックが発生する.
nil
チャンネルを閉じると同様にランタイムパニックが発生する.
close
を呼び出したあとで,
前に送った値が受信されたあと,
受信演算は,ブロックすることなしにチャンネルの型のゼロ値を返す.
多値受信演算は,
チャンネルが閉じられているかどうかの表示とともに受信値を返す.
ビルトイン関数 len
と cap
は,
多様な型の引数をとり,
型 int
の値を復帰する.
実装は,復帰値がいつでも int
に収まることを保証する
呼び出し 引数の型 復帰値
len(s) string 型 バイトでの文字列長
[n]T, *[n]T 配列長 (== n)
[]T スライス長
map[K]T マップ長 (定義されたキーの数)
chan T チャンネルバッファーにキューイングされた要素数
cap(s) [n]T, *[n]T 配列長 (== n)
[]T スライスの容量
chan T チャンネルバッファの容量
スライスの容量は, 基底配列で割り当てられた空間の要素数である. いつでも以下の関係が成り立つ.
0 <= len(s) <= cap(s)
nil
スライス,nil
マップ,nil
チャンネルの長さは 0 である.
nil
スライス,nil
チャンネルの容量は 0 である.
s
が文字列定数のとき
式 len(s)
は定数である.
s
の型が配列,配列へのポインターの型であり,
s
がチャンネル受信機か,
(非定数な)関数呼び出しを含まないとき,
式 len(s)
と式 cap(s)
は定数である.
このケースでは, s
は評価されない.
そうでなければ,
len
と cap
の呼び出しは,定数ではなく, s
は評価される.
const (
c1 = imag(2i) // imag(2i) = 2.0 は定数
c2 = len([10]float64{2}) // [10]float64{2} 関数呼び出しを含まない
c3 = len([10]float64{c1}) // [10]float64{c1} 関数呼び出しを含まない
c4 = len([10]float64{imag(2i)}) // imag(2i) は定数で,関数呼び出しの問題がない
c5 = len([10]float64{imag(z)}) // 無効: imag(z) は (非定数) 関数呼び出し
)
var z complex128
ビルトイン関数 new
は型 T
を取り,
実行時にその型の変数の格納域を割り当て (allocate),
それをポイントする型 *T
の値を復帰する.
変数は,初期値節で説明するように初期化される.
new(T)
例えば,
type S struct { a int; b float64 }
new(S)
は,型 S
の変数の保存域を割り当て,
それを初期化 (a=0
, b=0.0
) し,
その場所のアドレスを含む型 *S
の値を復帰する.
ビルトイン関数 make
は
スライス型,マップ型,またはチャンネル型 T
を受け取り,
オプションで,式の型固有のリストが続く.
(型 *T
ではなく) 型 T
の値を復帰する.
メモリは,
初期値節で説明するように初期化される.
呼び出し 型 T 復帰値
make(T, n) スライス 型 T, 長さ n, 容量 n のスライス
make(T, n, m) スライス 型 T, 長さ n, 容量 m のスライス
make(T) マップ 型 T のマップ
make(T, n) マップ 型 T,約 n 要素の初期容量をもつマップ
make(T) チャンネル 型 T のバッファなしチャンネル
make(T, n) チャンネル 型 T, バッファサイズ n のバッファ付きチャンネル
サイズ引数 n
と m
は整数型か,型なし定数でなければならない.
定数のサイズ引数は,非負であり,
型 int
の値で表現可能でなければならない.
それが型なし定数であると,それは型 int
が与えられる.
n
と m
の両方共与えられ,定数である場合,
n
は m
よりも大きくてはならない.
実行時に n
が負か m
よりも大きい場合,
ランタイムパニックが発生する.
s := make([]int, 10, 100) // スライス len(s) == 10, cap(s) == 100
s := make([]int, 1e3) // スライス len(s) == cap(s) == 1000
s := make([]int, 1<<63) // 不当: len(s) が型 int で表現不可能
s := make([]int, 10, 0) // 不当: len(s) > cap(s)
c := make(chan int, 10) // バッファサイズ 10 のチャンネル
m := make(map[string]int, 100) // 約 100 要素の初期スペースをもつマップ
マップ型でサイズヒント n
を引数とする make
の呼び出しは,
n
マップ要素を保持する初期スペースをもつマップを生成する.
詳細な挙動は実装依存である.
ビルトイン関数 append
と copy
は,一般的なスライス演算を支援する.
どちらの関数でも,復帰値は引数に参照されるメモリが重複しているかどうかに依存しない.
可変長引数の関数 append
は,
0 個以上の値 x
を
型 S
の s
に追加し,
型 S
の追加した結果のスライスを復帰する.
ここで,S
はスライス型でなければならない.
値 x
は,型 ...T
のパラメーターに渡される.
ここで, T
は S
の要素型であり,
それぞれパラメーター受け渡しのルールが適用される.
特別な場合として,
append
はまた,
最初の引数が,[]byte
型に代入可能で,
2 番めの引数が, 文字列型に ...
が続く場合も受け付ける.
この形式は文字列のバイトたちを追加する.
append(s S, x ...T) S // T は S の要素型
s
の容量が
追加する値に対して十分大きくない場合,
append
は新しく,
既存のスライスと追加の値を保持するのに
十分大きな基底配列を割り当てる.
そうでなければ,append
は基底配列を再利用する.
s0 := []int{0, 0}
s1 := append(s0, 2) // 一つの要素を追加 s1 == []int{0, 0, 2}
s2 := append(s1, 3, 5, 7) // 複数の要素を追加 s2 == []int{0, 0, 2, 3, 5, 7}
s3 := append(s2, s0...) // スライスを追加 s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}
s4 := append(s3[3:6], s3[2:]...) // 重複したスライスを追加 s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0}
var t []interface{}
t = append(t, 42, 3.1415, "foo") // t == []interface{}{42, 3.1415, "foo"}
var b []byte
b = append(b, "bar"...) // 文字列を追加 b == []byte{'b', 'a', 'r' }
関数 copy
は,
コピー元 (source) src
から,
コピー先 (destination) dst
へ
スライスの要素をコピーし,
コピーされた要素の数を復帰する.
どちらの引数も,
同一の要素型 T
を持たねばならず,
型 []T
のスライスへ代入可能でなければならない.
コピーされた要素の数は,len(src)
と len(dst)
の最小値である.
特別な場合として,
copy
は,
文字列型のコピー元引数(訳注:src
)を
[]byte
型のコピー先引数(訳注: dst
)への代入を受け入れる.
この形式では,文字列から,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
はマップ m
からキー k
の要素を削除する.
k
の型は m
のキー型に代入可能でなければならない.
delete(m, k) // マップ m から 要素 m[k] の削除
マップ m
は nil
であるか要素 m[k]
が存在しない場合,
delete
はなにもしない.
3 つの関数が,複素数を組み立て (assemble),分解する (disassemble).
ビルトイン関数 complex
は,
浮動小数点値の
実部 (real part) と虚部 (imaginary part) から
複素数値を構築する.
real
と imag
は,複素数値の
実部と虚部を抽出する.
complex(realPart, imaginaryPart floatT) complexT
real(complexT) floatT
imag(complexT) floatT
引数の型と復帰値の型は対応する.
complex
では,
2 つの引数は同じ浮動小数点型でなければならず,
復帰型は,対応する浮動小数点から成る複素数型である:
引数 float32
に対して, complex64
,
引数 float64
に対して, complex128
となる.
引数の一方が型なし定数に評価された場合,
それは最初に暗黙的に他方の引数の型に変換される.
どちらの引数も型なし定数に評価された場合,
それらは,
複素数でないか,虚部が 0 でなければならず,
関数の復帰値は,型なし複素数定数である.
real
と imag
では,
引数は,複素数型でなければならず,
復帰型は対応する複素数点型である.
引数 complex64
に対しては float32
,
引数 complex128
に対しては float64
となる.
引数が型なし定数に評価された場合,
それは,数でなければならず,
関数の復帰値は,型なし浮動小数点定数である.
real
と imag
関数の 2 つで,
complex
の逆関数を形成するため,
複素数型 Z
の値 z
の値に対して,
z == Z(complex(real(z), imag(z)))
が成り立つ.
これらの関数のオペランドがすべて定数である場合, 復帰値は定数である.
var a = complex(2, -2) // complex128
const b = complex(1.0, -1.4) // 型なし複素数定数 1 - 1.4i
x := float32(math.Cos(math.Pi/2)) // float32
var c64 = complex(5, -x) // complex64
var s int = complex(1, 0) // 型なし複素数定数 1 0i は int に変換できる
_ = complex(1, 2<<s) // 不当: 2 は浮動小数点型となり,シフトできない
var rl = real(c64) // float32
var im = imag(a) // float64
const c = imag(b) // 型なし定数 -1.4
_ = imag(3 << s) // 不当: 3 は複素数型となり,シフトできない
2 つのビルトイン関数 panic
と recover
はランタイムパニックと
プログラムで定義されたエラー条件の
レポートと処理を支援する.
func panic(interface{})
func recover() interface{}
関数 F
の実行中に,
明示的な panic
の呼び出しか,
ランタイムパニックは,
F
の実行を停止する.
F
によって遅延された任意の関数は,通常通り実行される.
次に,F
の呼び出し元によって遅延された関数が実行され,
実行中のゴルーチンのトップレベル関数によって遅延された関数が続く.
その時点で,
プログラムは終了し,
panic
の引数の値を含む
エラー条件を報告する.
この停止列 (termination sequence) は,パニック (panicking) と呼ばれる.
panic(42)
panic("到達しない unreachable")
panic(Error("解析できない cannot parse"))
recover
関数は,プログラムがパニックしたゴルーチンの動作を管理することを可能にする.
関数 G
が recover
を呼び出す関数 D
を遅延し,
パニックが G
を実行中に同じゴルーチン上の関数で発生すると仮定する.
遅延関数の実行中に D
に到達すると,
recover
を呼び出す D
の復帰値は,
panic
の呼び出しに渡された値である.
D
が新しい panic
を開始することなしに,通常通り復帰する場合,
パニック列は停止する.
その場合,
G
と panic
の呼び出しの間に呼び出される関数の状態が破棄され,
通常の実行が再開される.
D
の前に G
によって遅延された
任意の関数が実行され,
G
の実行がその実行元に戻ることによって終了する.
以下の条件のいずれかを満たすとき,
recover
の復帰値は nil
である.
panic
の引数がnil
である,または- ゴルーチンがパニックしていない,または
recover
が遅延関数から直接的によばれていない
以下の例の protect
関数は,
関数の引数 g
を呼び出し,
g
によって発生したランタイムパニックから
呼び出し元を保護する (protect).
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()
}
現在の実装では, ブートラップ (bootstrap) 中に役立ついくつかのビルトイン関数が提供される. これらの関数は,完全に文書化されているが, 言語が動作を保証するものではない. これらの関数は復帰値を持たない.
関数 動作
print すべての引数を出力する; 引数のフォーマットは実装依存である
println print ににているが,引数の間にスペースと,最後に改行を出力する
実装上の制限:
print
と println
は
任意の引数の型を受け取れる必要はないが,
ブール型,数値型,文字列型の出力はサポートしなければならない.
Go プログラムは,パッケージ (package) をリンクすることによって構築される. パッケージは, パッケージに属する定数,型,変数,関数を宣言し, 同じパッケージのすべてのファイルからアクセス可能な 1 つ以上のソースファイルから構築される. これらの要素は, エクスポートされ, 他のパッケージで使用される.
各ソースファイル (source file, SourceFile
) は,
それが属するパッケージを定義するパッケージ句 (package clause; PackageClause
) から成り,
それが使用するパッケージを宣言する import
宣言 ImportDecl
の(空でも良い)集合が続き,
関数,型,変数,定数の宣言 TopLevelDecl
の(空でも良い)集合が続く.
SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .
パッケージ句 (package clause) は各ソースファイルを開始し, そのファイルが属するパッケージを定義する.
PackageClause = "package" PackageName .
PackageName = identifier .
PackageName
はブランク識別子にはできない.
package math
同じ PackageName
を共有するファイルの集合は,
パッケージの実装を形成する.
実装は,パッケージに属する
すべてのソースファイルが
同じディレクトリに存在することを要求する場合がある.
インポート宣言 (import declaration; ImportDecl
) は,
その宣言を含むソースファイルがインポート (import) するパッケージの機能 (プログラムの初期化と実行節) に依存することを示し,
そのパッケージのエクスポートされた識別子にアクセスが可能となる.
インポートは,
アクセスのために使用される
識別子 (PackageName
) を名付け,
ImportPath
は
インポートされるパッケージを指定する.
ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
ImportSpec = [ "." | PackageName ] ImportPath .
ImportPath = string_lit .
PackageName
は
インポートいたソースファイル内で,
そのパッケージのエクスポートされた識別子にアクセスするために修飾識別子が使用される.
それは,ファイルブロック内で宣言される.
PackageName
が省略されると,
デフォルトで,
インポートされたパッケージのパッケージ句において指定された識別子になる.
明示的なピリオド .
が名前の代わりに使用される場合,
そのパッケージのパッケージブロックで宣言された
パッケージのエクスポートされたすべての識別子が,
インポートしたソースファイルのファイルブロックで宣言され,
修飾子なしでアクセスできる.
ImportPath
の解釈は,実装依存であるが,
通常は,
コンパイルされたパッケージのフルファイル名の部分文字列であり,
インストールされたパッケージのリポジトリに関連している場合がある.
実装上の制限:
コンパイラは,
Unicode の
L, M, N, P, S general categories に属する文字 (スペースを除く Graphic 文字) のみを使用した
空でない文字列に ImportPaths
を制限する場合があるし,
文字 !"#$%&'()*,:;<=>?[\]^
{|}と Unicode 置換文字
U FFFD` を除外する場合もある.
関数 Sin
をエクスポートする
package math
をパッケージ句に含むパッケージをコンパイルし,
"lib/math"
で特定されるファイルにコンパイルされたパッケージはインストールした
と仮定する.
以下の表は,
インポート宣言のさまざまな形式のあと,
そのパッケージをインポートするファイルで
どのように Sin
がファイル内でアクセスされるかを示す.
インポート宣言 Sin のローカル名
import "lib/math" math.Sin
import m "lib/math" m.Sin
import . "lib/math" Sin
インポート宣言は, インポートしたパッケージとインポートされたパッケージの間の依存関係を宣言する. 直接的でも,間接的でも, パッケージが自分自身をインポートすることは不当であり, パッケージがエクスポートした識別子を参照しないのに, 直接的にそのパッケージをインポートするのは不当である. 副作用 (初期化) のみを目的としてパッケージをインポートする場合は, 暗黙的なパッケージ名としてブランク識別子を使用する.
import _ "lib/math"
並行エラトステネスの篩 (prime sieve) を実装する 完全な Go パッケージである.
package main
import "fmt"
// チャンネル 'ch' に列 2, 3, 4, … を送信
func generate(ch chan<- int) {
for i := 2; ; i {
ch <- i // Send 'i' to channel 'ch'.
}
}
// 'prime' で割り切れる数を削除しながら,
// チャンネル 'src' からチャンネル 'dst' へ値をコピーする
func filter(src <-chan int, dst chan<- int, prime int) {
for i := range src { // 'src' から受け取った値を繰り返す
if i%prime != 0 {
dst <- i // 'i' をチャンネル 'dst' へ送信
}
}
}
// エラトステネスの篩: デイジーチェインフィルターも一緒に処理する
func sieve() {
ch := make(chan int) // 新しいチャンネルの生成
go generate(ch) // サブプロセスとして generate() を開始
for {
prime := <-ch
fmt.Print(prime, "\n")
ch1 := make(chan int)
go filter(ch, ch1, prime)
ch = ch1
}
}
func main() {
sieve()
}
宣言または, new
の呼び出しによって
格納域が変数として割り当てられるとき,または,
複合リテラルか make
の呼び出しによって
新しい変数が生成され,かつ,
明示的な初期化が提供されなかったとき,
変数や値はデフォルト値が与えられる.
そのような変数や値の要素には,
その型に対するゼロ値 (zero value) が設定される.
ゼロ値は,
ブールには False
,
数値型には 0
,
文字列には ""
,
ポインター,関数,インターフェース,スライス,チャンネル,マップには nil
である.
この初期化は,再帰的に行われ,
例えば,
構造体の配列の各要素が,
もし,初期値が設定されていなければ
各フィールドがゼロ値をもつ.
以下の 2 つの単純な宣言は等価である.
var i int
var i int = 0
さらに,
type T struct { i int; f float64; next *T }
t := new(T)
は,次を満足する.
t.i == 0
t.f == 0.0
t.next == nil
同じことが以下にも当てはまる.
var t T
パッケージ内では, パッケージレベルの変数の初期化が段階的に行われる. 初期化されていない変数に依存していない変数で, 各段階では, 宣言した順序 (declaration order) がはやい変数から選択される.
より正確には, パッケージレベルの変数は, それが,初期化されておらず,かつ, 初期化式を持たないか, その初期化式が未初期化の変数に依存 (dependency) していない場合, 初期化する準備ができた (ready for initialization) とよぶ. 初期化は, 次のパッケージレベルの変数を 宣言された順序で最もはやいもので,かつ,初期化される準備ができた変数 から 初期化する準備ができた変数がなくなるまで, 繰り返し初期化する.
すべての変数が未初期化のままである場合, この処理は終了する. これらの変数は,1 つ以上の初期化サイクルの一部であり, プログラムは有効でない.
右辺が 単一で多値の式によって初期化される変数宣言の 左辺の複数の変数は,同時に初期化される: 左辺の変数の一部が初期化されていたら, 他の変数たちも同時に初期化される.
var x = a
var a, b = f() // a と b は x の初期化前に,同時に初期化される
パッケージの初期化を目的として, ブランク変数が, 宣言における他の変数のように扱われる.
複数の変数で宣言された 変数の宣言順序は, コンパイラーに提示されたファイルの順で決定する. 最初のファイルで宣言された変数は, 2 番目のファイルで宣言される変数よりも前に宣言される,など.
依存関係分析 (dependency analysis) は,
変数の実際の値には依存せず,
推移的に分析される
ソースコード内のそれらの字句的な参照 (reference) にのみ依存する.
例えば,変数 x
の初期化式が,
変数 y
を本体で参照する関数を参照している場合,
x
は y
に依存する.
具体的には:
- 変数か関数への参照は,その変数か関数を示す識別子である.
- メソッド
m
への参照は,t
の(静的な) 型がインターフェース型でない, 形式t.m
のメソッド値またはメソッド式であり, メソッドm
はt
のメソッド集合に属する. 結果の関数値t.m
が呼び出されたかどうかは重要ではない. - 変数,関数,メソッド
x
の初期化式,または,本体(関数本体・メソッド本体)が,y
への参照y
に依存する関数かメソッドへの参照 を含む場合,x
は,y
に依存する
例えば,以下の宣言が与えられたとき,
var (
a = c b // == 9
b = f() // == 4
c = f() // == 5
d = 3 // == 5 (初期化が完了したあと)
)
func f() int {
d
return d
}
初期化順序は d
, b
, c
, a
である.
初期化式における部分式の順序は無関係である.
この例において,
a = c b
と a = b c
は,
同じ初期化順序になる.
依存関係分析は, パッケージ毎に行われる. 現在のパッケージ内で宣言される 変数,関数,(非インターフェース)メソッドへの参照のみ,考慮される. 他の隠れたデータ依存関係が変数間に存在する場合, これらの変数の初期化順序は指定されない.
例えば,以下の宣言が与えられたとき,
var x = I(T{}).ab() // x は検出されない,隠れた依存関係を a と b にもつ
var _ = sideEffect() // x, a, b とは無関係
var a = b
var b = 42
type I interface { ab() []int }
type T struct{}
func (T) ab() []int { return []int{a, b} }
変数 a
は b
の後に初期化されるば,
x
が b
の前に初期化されるか,
b
と a
の間に初期化されるか,
a
の後に初期化されるかどうか
したがって,sideEffect()
が呼び出されるタイミング (moment) も指定されていない.
変数は,
パッケージブロックで宣言される
関数名 init
を使って初期化される場合がある.
ここで,init
は引数と復帰値をもたない.
func init() { … }
単一のファイル内であっても複数のそのような関数がパッケージごとに定義できる.
パッケージブロックでは,
init
識別子は,init
関数の宣言にのみ使用され,
識別子自身は,宣言されない.
したがって,init
関数はプログラムのどこからも参照されない.
インポートがないパッケージは,
すべてのパッケージレベルの変数に初期値を代入し,
すべての init
関数をソースコード内で現れる順(複数ファイルの場合,コンパイラーに提示される順)に呼び出されることで,
初期化される.
あるパッケージがインポートをもつ場合,
インポートされるパッケージは,インポートするパッケージよりも前に初期化される.
あるパッケージを
複数のパッケージがインポートする場合,
インポートされるパッケージは,一度だけ初期化される.
パッケージのインポートにより,構造上,初期化の依存関係に循環がないことを保証できる.
パッケージの初期化 (変数初期化と init
関数の呼び出し)
一度にひとつのパッケージを
順に単一のゴルーチンで実行する.
init
関数は,
初期化コードと同時に実行でき,
他のゴルーチンを起動することがある.
しかし,
初期化はいつも init
関数を順に処理する.
つまり,
前の init
関数が復帰するまで
次の init
関数は呼び出されることはない.
初期化動作の再現性を保証するために, ビルドシステム (build system) は, 同一パッケージ内に属する複数のファイルを 字句ファイル名順にコンパイラに提示することを推奨する.
完全なプログラムは,
メインパッケージ (main package) と呼ばれる,
単一で,インポートされないパッケージを
メインパッケージがインポートするパッケージを推移的に (transitively)
リンクによって生成される.
メインパッケージは,パッケージ名 main
を持ち,
引数を取らず,復帰値のない main
関数を宣言しなければならない.
func main() { … }
プログラムの実行 (program execution) は,man パッケージの初期化から開始し,
関数 main
を呼び出す.
その関数呼び出しが復帰すると,関数は終了する (exist).
それは,他の (非 main
) ゴルーチンの完了を待たない.
事前宣言された型 error
は以下で定義される.
type error interface {
Error() string
}
これは,エラー条件を表すために便利なインターフェースである.
nil
値はエラーがないことを表す.
例えば,ファイルのデータを読み込む関数は以下のように定義される場合がある.
func Read(f *File, b []byte) (n int, err error)
配列の範囲外のインデックスの指定のような
実行時エラーはランタイムパニック (run-time panic) を引き起こす.
これは,
実装で定義されたインターフェース型 runtime.Error
の値で,
ビルトイン関数 panic
の呼び出しと等価である.
その型は,事前宣言されたインターフェース型 error
を満足する.
個別の実行時エラー条件を表すエラーの正確な値は指定されていない.
package runtime
type Error interface {
error
// それから,他のメソッド
}
コンパイラが認識し,
インポートパス unsafe
を通してアクセス可能な
ビルトイン関数 unsafe
は,
型システムに違反する操作を含む低レベルプログラミング機能を提供する.
unsafe
を使用するパッケージは,
型の安全性のために,手動で入念に検査する必要があり,
移植できない場合もある.
パッケージは以下のインターフェースを提供する.
package unsafe
type ArbitraryType int // 任意の Go 型の省略形; 本当の型ではない.
type Pointer *ArbitraryType
func Alignof(variable ArbitraryType) uintptr
func Offsetof(selector ArbitraryType) uintptr
func Sizeof(variable ArbitraryType) uintptr
Pointer
は,ポインター型であるが,
Pointer
値は,逆参照できない.
任意の基底型 uintptr
のポインターまたは値は,
基底型 Pointer
の型へ変換でき,その逆も可能である.
Point
と uintptr
間の変換の影響は実装定義である.
var f float64
bits = *(*uint64)(unsafe.Pointer(&f))
type ptr unsafe.Pointer
bits = *(*uint64)(ptr(&f))
var p ptr = nil
関数 Alignof
と Sizeof
は,それぞれ,
任意の型の式 x
を引数にとり,
v
が var v = x
を介して宣言されたかのように
仮の変数 v
の配置とサイズを復帰する.
関数 Offsetof
は,(丸括弧で囲まれた)セレクタ s.f
を引数にとる.
ここで,s.f
は s
または *s
によって表記される構造体のフィールド f
を表す.
関数 Offsetof
は構造体のアドレスを基準とした相対バイトで,フィールドオフセットを復帰する.
f
が埋め込みフィールドである場合,
構造体のフィールドを通してポインター間接参照なしで,
到達可能でなければならない.
フィールド f
をもつ構造体 s
に対して:
uintptr(unsafe.Pointer(&s)) unsafe.Offsetof(s.f) == uintptr(unsafe.Pointer(&s.f))
コンピュータ・アーキテクチャは,
メモリアドレスの整列を必要とする場合がある.
つまり,
変数のアドレスが因子の倍数であるために,
変数の型の整列が必要となる.
関数 Alignof
は任意の型の変数を表す式を引数にとり,
バイト単位で,変数の(型の)配置を返す.
変数 x
に対して,
uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0
Alignof
, Offsetof
, Sizeof
の呼び出しは,
型 uintptr
のコンパイル時定数式である.
数値型に対して,以下のサイズ (size) が保証される.
型 バイト単位のサイズ
byte, uint8, int8 1
uint16, int16 2
uint32, int32, float32 4
uint64, int64, float64, complex64 8
complex128 16
以下の最小限の配置 (alignment) 性質が保証される:
- 任意の型の変数
x
:unsafe.Alignof(x)
は少なくとも 1 である. - 構造体型の変数
x
:unsafe.Alignof(x)
はx
の各フィールドf
に対するunsafe.Alignof(x.f)
の最大値であり,少なくとも 1 である. - 配列型の変数
x
:unsafe.Alignof(x)
は配列の要素型の変数の配置と同じである.
サイズが 0 よりも大きなフィールドまたは要素を持たない 構造体型または配列型はサイズ 0 である. 2 つの異なる 0 サイズの経数は,メモリ上同じアドレスをもつ場合がある.