ステートメント
ステートメントは実行を制御します。
Statement = Declaration | LabeledStmt | SimpleStmt |
GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
DeferStmt .
SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .
終端ステートメント
終端ステートメントはブロック内の通常の制御フローを中断します。以下のステートメントは終端ステートメントです:
組み込み関数
panic
の呼び出し。ステートメントリストが終端ステートメントで終わるブロック。
以下の条件を満たす“if"ステートメント:
- “else"ブランチが存在する、かつ
- 両方のブランチが終端ステートメントである。
以下の条件を満たす“for"ステートメント:
- その"for"ステートメントを参照する"break"ステートメントがない、かつ
- ループ条件が存在しない、かつ
- “for"ステートメントがrange句を使用していない。
以下の条件を満たす“switch"ステートメント:
- その"switch"ステートメントを参照する"break"ステートメントがない、かつ
- デフォルトケースがある、かつ
- 各ケース(デフォルトを含む)のステートメントリストが終端ステートメントで終わるか、ラベル付きの可能性がある“fallthrough"ステートメントで終わる。
以下の条件を満たす“select"ステートメント:
- その"select"ステートメントを参照する"break"ステートメントがない、かつ
- 各ケース(存在する場合はデフォルトを含む)のステートメントリストが終端ステートメントで終わる。
終端ステートメントにラベル付けされたラベル付きステートメント。
その他のすべてのステートメントは終端ではありません。
ステートメントリストが空でなく、その最後の非空ステートメントが終端ステートメントである場合、そのリストは終端ステートメントで終わります。
空ステートメント
空ステートメントは何もしません。
EmptyStmt = .
ラベル付きステートメント
ラベル付きステートメントはgoto
、break
またはcontinue
ステートメントの対象となる場合があります。
LabeledStmt = Label ":" Statement .
Label = identifier .
Error: log.Panic("error encountered")
式ステートメント
特定の組み込み関数を除き、関数やメソッドの呼び出しおよび受信操作はステートメントコンテキストで現れることができます。このようなステートメントは括弧で囲むことができます。
ExpressionStmt = Expression .
以下の組み込み関数はステートメントコンテキストでは許可されていません:
append cap complex imag len make new real
unsafe.Add unsafe.Alignof unsafe.Offsetof unsafe.Sizeof unsafe.Slice unsafe.SliceData unsafe.String unsafe.StringData
h(x+y)
f.Close()
<-ch
(<-ch)
len("foo") // lenが組み込み関数の場合は不正
送信ステートメント
送信ステートメントはチャネルに値を送信します。チャネル式はチャネル型である必要があり、チャネル方向は送信操作を許可する必要があり、送信される値の型はチャネルの要素型に代入可能である必要があります。
SendStmt = Channel "<-" Expression .
Channel = Expression .
通信が始まる前に、チャネルと値の式の両方が評価されます。通信は送信が進行できるまでブロックします。
バッファなしチャネルへの送信は、レシーバーが準備できている場合に進行できます。
バッファありチャネルへの送信は、バッファに空きがある場合に進行できます。
閉じたチャネルへの送信は実行時パニックを引き起こします。
nil
チャネルへの送信は永遠にブロックします。
ch <- 3 // 値3をチャネルchに送信
チャネル式の型が型パラメータの場合、そのタイプセット内のすべての型は送信操作を許可するチャネル型である必要があり、すべて同じ要素型を持ち、送信される値の型はその要素型に代入可能である必要があります。
インクリメント・デクリメントステートメント
“++“と”–“ステートメントは、型なしの定数1
によってオペランドをインクリメントまたはデクリメントします。代入と同様に、オペランドはアドレス指定可能かマップインデックス式である必要があります。
IncDecStmt = Expression ( "++" | "--" ) .
以下の代入ステートメントは意味的に同等です:
インクリメント・デクリメントステートメント 代入
x++ x += 1
x-- x -= 1
代入ステートメント
代入は、変数に格納されている現在の値を式で指定された新しい値に置き換えます。代入ステートメントは、単一の値を単一の変数に代入するか、複数の値を対応する数の変数に代入することができます。
Assignment = ExpressionList assign_op ExpressionList .
assign_op = [ add_op | mul_op ] "=" .
各左辺オペランドはアドレス指定可能か、マップインデックス式、または(=
代入のみ)ブランク識別子である必要があります。オペランドは括弧で囲むことができます。
x = 1
*p = f()
a[i] = 23
(k) = <-ch // k = <-chと同じ
代入操作 x
op=
y
(opはバイナリ算術演算子)は、x
=
x
op (y)
と同等ですが、x
は一度だけ評価されます。op=
の構造は単一のトークンです。代入操作では、左辺と右辺の式リストの両方が正確に1つの単一値式を含む必要があり、左辺の式はブランク識別子であってはなりません。
a[i] <<= 2
i &^= 1<<n
タプル代入は、多値操作の個々の要素を変数のリストに代入します。2つの形式があります。最初の形式では、右辺オペランドは関数呼び出し、チャネルやマップ操作、または型アサーションなどの単一の多値式です。左辺のオペランドの数は値の数と一致する必要があります。例えば、f
が2つの値を返す関数の場合:
x, y = f()
最初の値がx
に、2番目がy
に代入されます。
2番目の形式では、左辺のオペランドの数は右辺の式の数と等しくなければならず、それぞれが単一値でなければならず、右辺のn番目の式は左辺のn番目のオペランドに代入されます:
one, two, three = '一', '二', '三'
ブランク識別子は代入における右辺の値を無視する方法を提供します:
_ = x // xを評価するが無視する
x, _ = f() // f()を評価するが2番目の結果値を無視する
代入は2つのフェーズで進行します。 まず、左辺のインデックス式とポインタ間接参照(セレクタの暗黙のポインタ間接参照を含む)のオペランドと右辺の式がすべて通常の順序で評価されます。 次に、代入が左から右の順序で実行されます。
a, b = b, a // aとbを交換
x := []int{1, 2, 3}
i := 0
i, x[i] = 1, 2 // i = 1, x[0] = 2を設定
i = 0
x[i], i = 2, 1 // x[0] = 2, i = 1を設定
x[0], x[0] = 1, 2 // x[0] = 1を設定し、その後x[0] = 2(つまり最終的にx[0] == 2)
x[1], x[3] = 4, 5 // x[1] = 4を設定し、その後x[3] = 5でパニック。
type Point struct { x, y int }
var p *Point
x[2], p.x = 6, 7 // x[2] = 6を設定し、その後p.x = 7でパニック
i = 2
x = []int{3, 5, 7}
for i, x[i] = range x { // i, x[2] = 0, x[0]を設定
break
}
// このループの後、i == 0かつxは[]int{3, 5, 3}
代入において、各値は代入先のオペランドの型に代入可能でなければなりません。以下の特殊なケースがあります:
どんな型付きの値もブランク識別子に代入できます。
型なし定数がインターフェース型の変数またはブランク識別子に代入される場合、その定数は最初に暗黙的にそのデフォルト型に変換されます。
型なしブール値がインターフェース型の変数またはブランク識別子に代入される場合、それは最初に暗黙的に
bool
型に変換されます。
値が変数に代入されるとき、変数に格納されているデータのみが置き換えられます。値に参照が含まれている場合、代入は参照をコピーしますが、参照されるデータ(スライスの基底配列など)のコピーは作成しません。
var s1 = []int{1, 2, 3}
var s2 = s1 // s2はs1のスライス記述子を格納
s1 = s1[:1] // s1の長さは1だが、依然としてs2と基底配列を共有している
s2[0] = 42 // s2[0]を設定すると、s1[0]も変更される
fmt.Println(s1, s2) // [42] [42 2 3]と出力
var m1 = make(map[string]int)
var m2 = m1 // m2はm1のマップ記述子を格納
m1["foo"] = 42 // m1["foo"]を設定すると、m2["foo"]も変更される
fmt.Println(m2["foo"]) // 42と出力
if文
“if"文は、ブール式の値に応じて2つのブランチの条件付き実行を指定します。式がtrueと評価された場合、“if"ブランチが実行され、そうでなければ、存在する場合は"else"ブランチが実行されます。
IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .
if x > max {
x = max
}
式の前に単純なステートメントを置くことができ、これは式が評価される前に実行されます。
if x := f(); x < y {
return x
} else if x > z {
return z
} else {
return y
}
スイッチ文
「switch」文は複数の実行経路を提供します。式または型は「switch」内の「case」と比較され、実行するブランチが決定されます。
SwitchStmt = ExprSwitchStmt | TypeSwitchStmt .
スイッチ文には2つの形式があります:式スイッチと型スイッチです。 式スイッチでは、caseに含まれる式がswitch式の値と比較されます。 型スイッチでは、caseに含まれる型が特別に注釈されたswitch式の型と比較されます。 スイッチ式はスイッチ文内で正確に1回評価されます。
式スイッチ
式スイッチでは、switch式が評価され、case式(定数である必要はありません)が左から右へ、上から下へと評価されます。switch式と等しい最初のcase式が、関連するcaseの文の実行をトリガーし、他のcaseはスキップされます。
いずれのcaseも一致せず、「default」caseがある場合は、その文が実行されます。
defaultケースは最大でも1つのみ許可され、「switch」文のどこにでも表示できます。
switch式が省略された場合は、論理値true
と同等になります。
ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
ExprCaseClause = ExprSwitchCase ":" StatementList .
ExprSwitchCase = "case" ExpressionList | "default" .
switch式が型付けされていない定数として評価される場合、最初に暗黙的にデフォルトの型に変換されます。
定義済みの型付けされていない値nil
はswitch式として使用できません。
switch式の型は比較可能である必要があります。
case式が型付けされていない場合、最初に暗黙的にswitch式の型に変換されます。
各(変換された可能性のある)case式x
とswitch式の値t
に対して、x == t
は有効な比較でなければなりません。
言い換えれば、switch式は、明示的な型なしで一時変数t
を宣言して初期化するのと同じように扱われます。各case式x
は、この値t
に対して等値性がテストされます。
caseまたはdefault句では、最後の空でない文が(ラベル付けされている可能性のある)fallthrough文となり、このclauseの終わりから次のclauseの最初の文へと制御が流れることを示すことができます。 それ以外の場合、制御は「switch」文の終わりに流れます。「fallthrough」文は、式スイッチの最後のclauseを除くすべてのclauseの最後の文として表示できます。
switch式の前に単純文を置くことができ、式の評価前に実行されます。
switch tag {
default: s3()
case 0, 1, 2, 3: s1()
case 4, 5, 6, 7: s2()
}
switch x := f(); { // switch式が省略されている場合は "true" を意味する
case x < 0: return -x
default: return x
}
switch {
case x < y: f1()
case x < z: f2()
case x == 4: f3()
}
実装制限:コンパイラは、同じ定数に評価される複数のcase式を禁止する場合があります。 例えば、現在のコンパイラはcase式内の重複する整数、浮動小数点、または文字列定数を禁止しています。
型スイッチ
型スイッチは値ではなく型を比較します。それ以外は式スイッチに似ています。実際の型ではなくtype
キーワードを使用した型アサーションの形式の特別なswitch式によってマークされます:
switch x.(type) {
// cases
}
caseは実際の型T
を式x
の動的型と照合します。型アサーションと同様に、x
はインターフェース型である必要がありますが、型パラメータではありません。また、caseにリストされている各非インターフェース型T
はx
の型を実装している必要があります。
型スイッチのcaseにリストされる型はすべて異なる必要があります。
TypeSwitchStmt = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" .
TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
TypeCaseClause = TypeSwitchCase ":" StatementList .
TypeSwitchCase = "case" TypeList | "default" .
TypeSwitchGuardには短い変数宣言を含めることができます。 この形式が使用される場合、変数は各節の暗黙的なブロックのTypeSwitchCaseの末尾で宣言されます。 正確に1つの型をリストするcase句では、変数はその型を持ちます。それ以外の場合、変数はTypeSwitchGuard内の式の型を持ちます。
型の代わりに、caseは予め宣言された識別子nil
を使用することができます。
このcaseは、TypeSwitchGuard内の式がnil
インターフェース値である場合に選択されます。
nil
のcaseは最大でも1つしか存在できません。
型interface{}
の式x
があると仮定すると、次の型スイッチは:
switch i := x.(type) {
case nil:
printString("x is nil") // iの型はxの型(interface{})
case int:
printInt(i) // iの型はint
case float64:
printFloat64(i) // iの型はfloat64
case func(int) float64:
printFunction(i) // iの型はfunc(int) float64
case bool, string:
printString("type is bool or string") // iの型はxの型(interface{})
default:
printString("don't know the type") // iの型はxの型(interface{})
}
以下のように書き直すことができます:
v := x // xは正確に一度評価される
if v == nil {
i := v // iの型はxの型(interface{})
printString("x is nil")
} else if i, isInt := v.(int); isInt {
printInt(i) // iの型はint
} else if i, isFloat64 := v.(float64); isFloat64 {
printFloat64(i) // iの型はfloat64
} else if i, isFunc := v.(func(int) float64); isFunc {
printFunction(i) // iの型はfunc(int) float64
} else {
_, isBool := v.(bool)
_, isString := v.(string)
if isBool || isString {
i := v // iの型はxの型(interface{})
printString("type is bool or string")
} else {
i := v // iの型はxの型(interface{})
printString("don't know the type")
}
}
型パラメータまたはジェネリック型をcaseの型として使用できます。インスタンス化時にその型がスイッチ内の別のエントリと重複する場合、最初に一致するcaseが選択されます。
func f[P any](x any) int {
switch x.(type) {
case P:
return 0
case string:
return 1
case []P:
return 2
case []byte:
return 3
default:
return 4
}
}
var v1 = f[string]("foo") // v1 == 0
var v2 = f[byte]([]byte{}) // v2 == 2
型スイッチガードの前に単純文を置くことができ、ガードの評価前に実行されます。
「fallthrough」文は型スイッチでは許可されていません。
for文
「for」文はブロックの繰り返し実行を指定します。3つの形式があります: 繰り返しは単一の条件、「for」句、または「range」句によって制御できます。
ForStmt = "for" [ Condition | ForClause | RangeClause ] Block .
Condition = Expression .
単一条件付きのfor文
最も単純な形式では、「for」文はブール条件が真と評価される限り、ブロックの繰り返し実行を指定します。
条件は各繰り返しの前に評価されます。
条件が省略された場合、それはブール値true
と同等です。
for a < b {
a *= 2
}
for
句付きのfor文
ForClause付きの「for」文も条件によって制御されますが、 追加して代入、インクリメントまたはデクリメント文などの初期化(init)文と後処理(post)文を指定できます。init文は短い変数宣言かもしれませんが、post文はそうではありません。
ForClause = [ InitStmt ] ";" [ Condition ] ";" [ PostStmt ] .
InitStmt = SimpleStmt .
PostStmt = SimpleStmt .
for i := 0; i < 10; i++ {
f(i)
}
空でない場合、init文は最初の繰り返しの条件を評価する前に一度だけ実行されます。
post文はブロックの各実行後に実行されます(そしてブロックが実行された場合のみ)。
ForClauseの任意の要素は空でもよいですが、条件のみが存在する場合を除き、セミコロンは必須です。
条件が省略された場合は、ブール値true
と同等です。
for cond { S() } は for ; cond ; { S() } と同じ
for { S() } は for true { S() } と同じ
各繰り返しは独自の宣言された変数(または複数の変数)を持ちます(Go 1.22)。 最初の繰り返しで使用される変数はinit文によって宣言されます。 それ以降の各繰り返しで使用される変数は、post文の実行前に暗黙的に宣言され、その時点での前の繰り返しの変数の値で初期化されます。
var prints []func()
for i := 0; i < 5; i++ {
prints = append(prints, func() { println(i) })
i++
}
for _, p := range prints {
p()
}
これは以下を出力します:
1
3
5
Go 1.22以前は、繰り返しは独自の別々の変数を持つ代わりに1セットの変数を共有していました。 その場合、上の例は以下を出力します:
6
6
6
range
句付きのfor文
「range」句付きの「for」文は、配列、スライス、文字列またはマップのすべてのエントリ、チャネルで受信した値、ゼロから上限までの整数値(Go 1.22)、またはイテレータ関数のyield関数に渡された値(Go 1.23)を繰り返し処理します。 各エントリに対して、存在する場合は対応する繰り返し変数に繰り返し値を割り当て、その後ブロックを実行します。
RangeClause = [ ExpressionList "=" | IdentifierList ":=" ] "range" Expression .
「range」句の右側の式は「range式」と呼ばれ、配列、配列へのポインタ、スライス、文字列、マップ、受信操作を許可するチャネル、整数、または特定のシグネチャを持つ関数(以下参照)のいずれかです。 代入と同様に、左側のオペランドが存在する場合はアドレス指定可能またはマップのインデックス式でなければなりません。それらは繰り返し変数を示します。 range式が関数の場合、繰り返し変数の最大数は関数のシグネチャによって異なります。 range式がチャネルまたは整数の場合、最大でも1つの繰り返し変数が許可されます。 それ以外の場合は最大で2つの繰り返し変数が存在できます。 最後の繰り返し変数がブランク識別子の場合、range句はその識別子のない同じ句と同等です。
range式x
は、ループを開始する前に評価されます。ただし、1つ以下の繰り返し変数が存在し、x
またはlen(x)
が定数である場合は例外です。この場合、range式は評価されません。
左側の関数呼び出しは繰り返しごとに1回評価されます。 各繰り返しについて、対応する繰り返し変数が存在する場合、繰り返し値は次のように生成されます:
Range式 第1の値 第2の値
配列またはスライス a [n]E, *[n]E, または []E インデックス i int a[i] E
文字列 s string型 インデックス i int 下記参照 rune
マップ m map[K]V キー k K m[k] V
チャネル c chan E, <-chan E 要素 e E
整数値 n 整数型、または型なし整数 値 i 下記参照
関数、0個の値 f func(func() bool)
関数、1個の値 f func(func(V) bool) 値 v V
関数、2個の値 f func(func(K, V) bool) キー k K v V
配列、配列へのポインタ、またはスライス値
a
の場合、インデックス繰り返し値は要素インデックス0から始まる増加順で生成されます。 最大で1つの繰り返し変数が存在する場合、rangeループは0からlen(a)-1
までの繰り返し値を生成し、配列またはスライス自体にはインデックスを付けません。nil
スライスの場合、繰り返しの数は0です。文字列値の場合、「range」句はバイトインデックス0から始まる文字列内のUnicodeコードポイントを繰り返し処理します。連続する繰り返しでは、インデックス値は文字列内の連続するUTF-8エンコードされたコードポイントの最初のバイトのインデックスとなり、
rune
型の2番目の値は対応するコードポイントの値となります。繰り返しが無効なUTF-8シーケンスに遭遇した場合、2番目の値は0xFFFD
(Unicodeの置換文字)となり、次の繰り返しでは文字列内の単一バイトが進みます。マップの繰り返し順序は指定されておらず、ある繰り返しから次の繰り返しまで同じであることは保証されていません。 まだ到達していないマップエントリが繰り返し中に削除された場合、対応する繰り返し値は生成されません。マップエントリが繰り返し中に作成された場合、そのエントリは繰り返し中に生成されるか、スキップされる可能性があります。この選択は作成された各エントリによって異なり、繰り返しごとに変わる可能性があります。 マップが
nil
の場合、繰り返しの数は0です。チャネルの場合、生成される繰り返し値は、チャネルがクローズされるまでにチャネルに送信された連続した値です。チャネルが
nil
の場合、range式は永遠にブロックします。整数値
n
の場合、n
が整数型または型なしの整数定数である場合、繰り返し値0からn-1
が増加順で生成されます。n
が整数型の場合、繰り返し値はその同じ型を持ちます。 それ以外の場合、n
の型は繰り返し変数に代入されるかのように決定されます。 具体的には: 繰り返し変数が既存のものである場合、繰り返し値の型は繰り返し変数の型であり、これは整数型でなければなりません。 それ以外の場合、繰り返し変数が「range」句によって宣言されるか存在しない場合、繰り返し値の型はn
のデフォルト型です。n
<= 0の場合、ループは繰り返しを実行しません。関数
f
の場合、繰り返しは引数として新しく合成されたyield
関数をf
に渡すことによって進行します。f
が戻る前にyield
が呼び出された場合、yield
への引数はループ本体を一度実行するための繰り返し値になります。 各連続したループ繰り返しの後、yield
はtrueを返し、ループを続行するために再び呼び出される可能性があります。 ループ本体が終了しない限り、「range」句はf
が戻るまで、各yield
呼び出しに対してこの方法で繰り返し値を生成し続けます。 ループ本体が終了した場合(例えばbreak
文による)、yield
はfalseを返し、再び呼び出されてはなりません。
繰り返し変数は、短い変数宣言の形式(:=
)を使用して「range」句によって宣言できます。
この場合、それらのスコープは「for」文のブロックであり、各繰り返しは独自の新しい変数を持ちます(Go 1.22)(ForClause付きの「for」文も参照)。
変数はそれぞれの繰り返し値の型を持ちます。
繰り返し変数が「range」句によって明示的に宣言されていない場合、それらは既存のものでなければなりません。 この場合、繰り返し値は代入文のように、それぞれの変数に割り当てられます。
var testdata *struct {
a *[7]int
}
for i, _ := range testdata.a {
// testdata.aは評価されない。len(testdata.a)は定数
// iは0から6までの範囲
f(i)
}
var a [10]string
for i, s := range a {
// iの型はint
// sの型はstring
// s == a[i]
g(i, s)
}
var key string
var val interface{} // mの要素型はvalに代入可能
m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6}
for key, val = range m {
h(key, val)
}
// key == 繰り返しで最後に遭遇したマップキー
// val == map[key]
var ch chan Work = producer()
for w := range ch {
doWork(w)
}
// チャネルを空にする
for range ch {}
// f(0), f(1), ... f(9)を呼び出す
for i := range 10 {
// iの型はint(型なし定数10のデフォルト型)
f(i)
}
// 無効:256はuint8に代入できない
var u uint8
for u = range 256 {
}
// 無効:1e3は浮動小数点定数
for range 1e3 {
}
// fiboはフィボナッチ数列を生成する
fibo := func(yield func(x int) bool) {
f0, f1 := 0, 1
for yield(f0) {
f0, f1 = f1, f0+f1
}
}
// 1000未満のフィボナッチ数を出力:
for x := range fibo {
if x >= 1000 {
break
}
fmt.Printf("%d ", x)
}
// 出力: 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
// 再帰的ツリーデータ構造の繰り返しサポート
type Tree[K cmp.Ordered, V any] struct {
left, right *Tree[K, V]
key K
value V
}
func (t *Tree[K, V]) walk(yield func(key K, val V) bool) bool {
return t == nil || t.left.walk(yield) && yield(t.key, t.value) && t.right.walk(yield)
}
func (t *Tree[K, V]) Walk(yield func(key K, val V) bool) {
t.walk(yield)
}
// ツリーtを中間順で走査
var t Tree[string, int]
for k, v := range t.Walk {
// k, vを処理
}
range式の型が型パラメータである場合、その型セット内のすべての型は同じ基底型を持ち、range式はその型に対して有効でなければなりません。または、型セットにチャネル型が含まれる場合、それは同一の要素型を持つチャネル型のみを含み、すべてのチャネル型は受信操作を許可しなければなりません。
Go文
「go」文は、同じアドレス空間内の独立した並行実行スレッド、または「ゴルーチン」として、関数呼び出しの実行を開始します。
GoStmt = "go" Expression .
式は関数またはメソッド呼び出しでなければなりません。それは括弧で囲むことはできません。 組み込み関数の呼び出しは、式文と同様に制限されています。
関数値とパラメータは呼び出しゴルーチンで通常どおりに評価されますが、 通常の呼び出しとは異なり、プログラムの実行は呼び出された関数が完了するのを待ちません。 代わりに、関数は新しいゴルーチンで独立して実行を開始します。 関数が終了すると、そのゴルーチンも終了します。 関数に戻り値がある場合、関数が完了すると破棄されます。
go Server()
go func(ch chan<- bool) { for { sleep(10); ch <- true }} (c)
selectステートメント
「select」ステートメントは、可能な送信または受信操作のセットから、どの操作を実行するかを選択します。これは「switch」ステートメントに似ていますが、すべてのケースが通信操作を参照している点が異なります。
SelectStmt = "select" "{" { CommClause } "}" .
CommClause = CommCase ":" StatementList .
CommCase = "case" ( SendStmt | RecvStmt ) | "default" .
RecvStmt = [ ExpressionList "=" | IdentifierList ":=" ] RecvExpr .
RecvExpr = Expression .
RecvStmtを持つケースでは、RecvExprの結果を1つまたは2つの変数に代入することができます。これらの変数は短い変数宣言を使用して宣言できます。RecvExprは(括弧で囲まれている可能性のある)受信操作でなければなりません。defaultケースは最大で1つだけ存在でき、ケースリストのどこにでも配置できます。
「select」ステートメントの実行は、いくつかのステップで進行します:
ステートメント内のすべてのケースに対して、受信操作のチャネルオペランドと送信ステートメントのチャネルおよび右辺式は、「select」ステートメントに入った時点で、ソース順に一度だけ評価されます。結果は、受信元または送信先のチャネルのセットとそれに対応する送信値です。この評価における副作用は、どの通信操作が選択されるかに関係なく発生します。短い変数宣言または代入を伴うRecvStmtの左辺の式はまだ評価されていません。
1つ以上の通信が進行できる場合、進行可能な1つがランダムに選択されます。そうでなく、defaultケースがある場合は、そのケースが選択されます。defaultケースがない場合、「select」ステートメントは少なくとも1つの通信が進行できるようになるまでブロックします。
選択されたケースがdefaultケースでない限り、それぞれの通信操作が実行されます。
選択されたケースが短い変数宣言または代入を伴うRecvStmtの場合、左辺の式が評価され、受信した値が代入されます。
選択されたケースのステートメントリストが実行されます。
nil
チャネルでの通信は決して進行できないため、nil
チャネルのみでdefaultケースがないselectは永遠にブロックします。
var a []int
var c, c1, c2, c3, c4 chan int
var i1, i2 int
select {
case i1 = <-c1:
print("received ", i1, " from c1\n")
case c2 <- i2:
print("sent ", i2, " to c2\n")
case i3, ok := (<-c3): // i3, ok := <-c3 と同じ
if ok {
print("received ", i3, " from c3\n")
} else {
print("c3 is closed\n")
}
case a[f()] = <-c4:
// 次と同じ:
// case t := <-c4
// a[f()] = t
default:
print("no communication\n")
}
for { // ランダムなビット列をcに送信
select {
case c <- 0: // 注意:ステートメントなし、フォールスルーなし、ケースの折りたたみなし
case c <- 1:
}
}
select {} // 永遠にブロック
returnステートメント
関数F
内の「return」ステートメントは、F
の実行を終了し、オプションで1つ以上の結果値を提供します。F
によって遅延された関数は、F
が呼び出し元に戻る前に実行されます。
ReturnStmt = "return" [ ExpressionList ] .
結果型のない関数では、「return」ステートメントは結果値を指定してはいけません。
func noResult() {
return
}
結果型を持つ関数から値を返す方法は3つあります:
- 「return」ステートメントで戻り値を明示的にリストできます。各式は単一値であり、関数の結果型の対応する要素に代入可能でなければなりません。
func simpleF() int {
return 2
}
func complexF1() (re float64, im float64) {
return -7.0, -4.0
}
- 「return」ステートメントの式リストは、複数の値を返す関数への単一の呼び出しであることがあります。その効果は、その関数から返される各値がそれぞれの値の型を持つ一時変数に代入され、その後これらの変数をリストする「return」ステートメントが続くかのようになります。この時点で前のケースのルールが適用されます。
func complexF2() (re float64, im float64) {
return complexF1()
}
- 関数の結果型がその結果パラメータに名前を指定している場合、式リストは空であってもかまいません。結果パラメータは通常のローカル変数として機能し、関数は必要に応じてそれらに値を代入することができます。「return」ステートメントはこれらの変数の値を返します。
func complexF3() (re float64, im float64) {
re = 7.0
im = 4.0
return
}
func (devnull) Write(p []byte) (n int, _ error) {
n = len(p)
return
}
宣言方法に関わらず、すべての結果値は関数に入った時点でその型のゼロ値に初期化されます。結果を指定する「return」ステートメントは、遅延関数が実行される前に結果パラメータを設定します。
実装上の制限:結果パラメータと同じ名前の別のエンティティ(定数、型、または変数)が戻り値のスコープ内にある場合、コンパイラは「return」ステートメントでの空の式リストを禁止することがあります。
func f(n int) (res int, err error) {
if _, err := f(n-1); err != nil {
return // 無効なreturnステートメント:errは影になっている
}
return
}
breakステートメント
「break」ステートメントは、同じ関数内の最も内側の「for」、「switch」、または「select」ステートメントの実行を終了します。
BreakStmt = "break" [ Label ] .
ラベルがある場合、それは囲む「for」、「switch」、または「select」ステートメントのものでなければならず、その実行が終了します。
OuterLoop:
for i = 0; i < n; i++ {
for j = 0; j < m; j++ {
switch a[i][j] {
case nil:
state = Error
break OuterLoop
case item:
state = Found
break OuterLoop
}
}
}
continueステートメント
「continue」ステートメントは、制御をループブロックの終わりに進めることで、最も内側の「for」ループの次の反復を開始します。「for」ループは同じ関数内になければなりません。
ContinueStmt = "continue" [ Label ] .
ラベルがある場合、それは囲む「for」ステートメントのものでなければならず、その実行が進められます。
RowLoop:
for y, row := range rows {
for x, data := range row {
if data == endOfRow {
continue RowLoop
}
row[x] = data + bias(x, y)
}
}
gotoステートメント
「goto」ステートメントは、同じ関数内の対応するラベルを持つステートメントに制御を転送します。
GotoStmt = "goto" Label .
goto Error
「goto」ステートメントの実行により、goto時点ですでにスコープ内にない変数がスコープに入ることがあってはなりません。例えば:
goto L // BAD
v := 3
L:
これは、ラベルL
へのジャンプがv
の作成をスキップするため、誤りです。
ブロック外の「goto」ステートメントは、そのブロック内のラベルにジャンプできません。例えば:
if n%2 == 1 {
goto L1
}
for n > 0 {
f()
n--
L1:
f()
n--
}
これは、ラベルL1
が「for」ステートメントのブロック内にあるのに対し、goto
はそうではないため、誤りです。
fallthroughステートメント
「fallthrough」ステートメントは、式「switch」ステートメント内の次のcase句の最初のステートメントに制御を転送します。このステートメントは、そのようなclause内の最後の非空ステートメントとしてのみ使用できます。
FallthroughStmt = "fallthrough" .
deferステートメント
「defer」ステートメントは、周囲の関数がreturnステートメントを実行した、関数本体の終わりに達した、または対応するゴルーチンがパニックしているために、周囲の関数が戻る瞬間まで実行が遅延される関数を呼び出します。
DeferStmt = "defer" Expression .
式は関数またはメソッド呼び出しでなければならず、括弧で囲むことはできません。組み込み関数の呼び出しは、式ステートメントと同様に制限されています。
「defer」ステートメントが実行されるたびに、関数値と呼び出しのパラメータは通常通り評価され、新たに保存されますが、実際の関数は呼び出されません。代わりに、遅延関数は周囲の関数が戻る直前に、遅延された逆順で呼び出されます。つまり、周囲の関数が明示的なreturnステートメントを通じて戻る場合、遅延関数はそのreturnステートメントによって結果パラメータが設定された後、しかし関数が呼び出し元に戻る前に実行されます。遅延関数値がnil
と評価される場合、「defer」ステートメントが実行されるときではなく、関数が呼び出されるときにパニックが発生します。
例えば、遅延関数が関数リテラルであり、周囲の関数がリテラル内でスコープにある名前付き結果パラメータを持つ場合、遅延関数は結果パラメータが返される前にアクセスして変更することができます。遅延関数が戻り値を持つ場合、関数が完了するとそれらは破棄されます。(パニックの処理に関するセクションも参照)
lock(l)
defer unlock(l) // ロック解除は周囲の関数が戻る前に発生
// 周囲の関数が戻る前に3 2 1 0を印刷
for i := 0; i <= 3; i++ {
defer fmt.Print(i)
}
// fは42を返す
func f() (result int) {
defer func() {
// resultはreturnステートメントで6に設定された後にアクセスされる
result *= 7
}()
return 6
}