Go/文

カテゴリ:Book:Go#文%20

文(Statements)は、実行をコントロールします[1]

構文
Statement =
	Declaration | LabeledStmt | SimpleStmt |
	GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
	FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
	DeferStmt ;
 
SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl ;

単純な文

単純な文(SimpleStmt)は、空文(EmptyStmt)、式文(ExpressionStmt)、Send文(SendStmt)、インクリメント・デクリメント文(IncDecStmt)、代入文(Assignment)、短い変数宣言(ShortVarDecl)の総称です。

単純な文(SimpleStmt)は、If文For文Switch文のそれぞれの条件式の直前に置くことができ、短い変数宣言(ShortVarDecl)の場合は宣言された変数のスコープは、それぞれの文になります。

終端文

終端文(A terminating statement)は、ブロック内の通常の制御の流れを中断します。 以下の文が終端文です[2]

  1. "return" または "goto" 文
  2. 組込み関数 panic の呼び出し
  3. 文リストが終端文で終わるブロック
  4. "if" 文の中の文
    • "else " 節が存在し、かつ
    • 両方の節が終端する文
  5. "for" 文では、次のようになります
    • "for" 文を参照する "break" 文が存在せず
    • ループ条件が存在し
      • かつ range 句が存在しない
  6. "switch" 文では
    • "switch "文を参照する "break" 文が存在しない場合。
    • デフォルトのケースが存在しており
    • デフォルトケースを含む各ケースの文リストは、終端文で終わるか、"fallthrough" 文というラベルが貼られている場合。
  7. select文の場合
    • "select" 文を参照する "break" 文が存在しない場合。
    • 各ケースの文リストは、デフォルトがある場合はそれも含めて、終端文で終わります。
  8. ラベル付きの文は、終端する文をラベリングします。

その他の文は終端しません。

文リストは、リストが空でなく、空でない最後の文が終端する場合、終端文で終わります。

空文

空文は何もしません[3]

構文
EmptyStmt = ;

ラベル付き文

ラベル付き文(A labeled statement)は、goto、break、continue 文のターゲットになることがあります[4]

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

式文

式文(Expression statements)[5]

特定の組込み関数を除いて、関数やメソッドの呼び出しや受信操作は文のコンテキストで表示できます。そのようなステートメントは括弧で囲まれていてもかまいません。

構文
ExpressionStmt = Expression ;

以下の組込み関数は、式文のコンテキストでは使用できません[6]

式文のコンテキストでは使用できない組込み関数
append cap complex imag len make new real 
unsafe.Add unsafe.Alignof unsafe.Offsetof unsafe.Sizeof unsafe.Slice
h(x+y)
f.Close()
<-ch
(<-ch) 
len("foo")  // lenが組み込み関数の場合は不正

Send文

send文(A send statement)は、チャンネル上で値を送信します。チャネル式はチャネル型でなければならず、チャネルの方向は送信操作を許可するものでなければならず、送信される値の型はチャネルの要素型に割り当て可能でなければなりません[7]

構文
SendStmt = Channel "<-" Expression ;
Channel  = Expression ;

通信が始まる前に、チャネルと値の式の両方が評価されます。通信は、送信を続行できるまでブロックされます。バッファリングされていないチャネルでの送信は、受信者の準備ができていれば続行できます。バッファ付きチャネルでの送信は、バッファに余裕があれば続行できます。閉じたチャネルへの送信は、ランタイムパニックを起こして進行します。nil チャネルへの送信は永遠にブロックされます。

ch <- 3  // チャンネルchに値3を送信

インクリメント・デクリメント文

”++"および"--"文(The "++" and "--" statements)は、オペランドを型付けされていない定数1だけ増加または減少させます。代入と同様に、オペランドはアドレス指定可能か、マップのインデックス式でなければなりません[8]

構文
IncDecStmt = Expression ( "++" | "--" ) ;
インクリメント・デクリメント文
インクリメント・デクリメント文簡略代入代入
x++x += 1x = x + 1
x--x -= 1x = x - 1

代入文

代入文(Assignments)[9]

構文
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 つのトークンです。 代入演算では、左辺と右辺の式リストの両方に、正確に1つの単一値の式が含まれていなければならず、左辺の式は空白の識別子であってはなりません。

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

タプルの代入は、多値演算の個々の要素を変数のリストに代入されます。2つの形式があります。 1つ目の形式では、右側のオペランドは、関数呼び出し、チャネルまたはマップ操作、またはタイプアサーションなどの単一の多値式です。 左辺側のオペランドの数は、値の数と一致しなければなりません。例えば、fが2つの値を返す関数の場合。

x, y = f()

は、第1の値を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 and x == []int{3, 5, 3}となります。

代入では、各値は代入されるオペランドのタイプに代入可能でなければなりませんが、以下の特別なケースがあります。

  1. ブランク識別子には、任意の型付けされた値を代入することができます。
  2. 型付けされていない定数がインタフェース型の変数や空白の識別子に代入されられた場合、その定数はまずデフォルトの型に暗黙的に変換されます。
  3. 型付けされていない真理値がインターフェイス型の変数や空白の識別子に代入されられた場合、まず暗黙のうちにbool型に変換されます。

If文

If文("If" statements )は、論理式の値に応じて、2つの節の条件付き実行を指定します。式の評価値がtrueの場合、"if" 節が実行され、そうでない場合は、"else" 節が実行されます[10]

構文
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" 文("Switch" statements)は多方向の実行を可能にします。式や型は、"switch" 内の "case" と比較され、どの分岐を実行するかが決定されます[11]

構文
SwitchStmt = ExprSwitchStmt | TypeSwitchStmt ;

式スイッチと型スイッチの2つの形式があります。 式スイッチでは、caseにはswitch文の条件式(以下、switch式)の値と比較される式が含まれています。 型スイッチでは、caseに型が含まれ、特別に注釈されたswitch式の型と比較されます。 switch式はswitch文の中で一度だけ評価されます。

式スイッチ

式スイッチ(an expression switch)では、switch式が評価され、定数でなくてもよいcase式が左から右、上から下へと評価され、最初にswitch式と一致したものが、関連するcaseのステートメントを実行するきっかけとなります。 一致するcaseがなく、"default" のcaseがある場合は、そのステートメントが実行されます。 "default" のcaseは最大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の宣言と初期化に使用されたように扱われ、そのtの値に対して各case式xの等質性がテストされます。

case節やdefault節では、最後の空ではない文を(ラベルを付けて)fallthrough文とすることで、この節の終わりから次の節の最初の文に制御が流れることを示します。 そうでなければ、制御はswitch文の最後まで流れます。fallthrough」文は、switch式の最後の節を除くすべての節の最後の文として表示されることがあります。

switch式の前には、式が評価される前に実行される単純な文を置くことができます。

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

switch x := f(); {  // missing switch expression means "true"
case x < 0: return -x
default: return x
}

switch {
case x < y: f1()
case x < z: f2()
case x == 4: f3()
}
実装上の制限
コンパイラーは、同じ定数に評価される複数の case 式を許可しない場合があります。例えば、現在のコンパイラーでは、整数、浮動小数点、文字列の各定数を大文字で表現することはできません。

型スイッチ

型スイッチ(A type switch)は、値ではなく型を比較します。それ以外の点では、式スイッチに似ています。これは、実際の型ではなくキーワードtypeを使用した型アサーションの形式を持つ、特別なスイッチ式によって示されます。

switch x.(type) {
// cases 
}

型アサーションと同様に、xはインターフェース型でなければならず、caseにリストアップされた非インターフェース型Tはそれぞれxの型を実装していなければなりません。

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

TypeSwitchGuardには、短い変数宣言を含めることができる。この形式が使用された場合、変数は各節の暗黙のブロックにあるTypeSwitchCaseの最後に宣言されます。正確に1つの型が記載されているcaseを持つ節では、変数はその型を持ちます。そうでない場合は、変数はTypeSwitchGuard内の式の型を持ちます。

それ以外の場合は、TypeSwitchGuard内の式の型を持つ変数となります。このcaseは、TypeSwitchGuard内の式がnilのインターフェース値である場合に選択されます。nilのcaseは最大1つです。

interface{}型の式xが与えられた場合、以下の型スイッチを行います。

switch i := x.(type) {
case nil:
	printString("x is nil")                // type of i is type of x (interface{})
case int:
	printInt(i)                            // type of i is int
case float64:
	printFloat64(i)                        // type of i is float64
case func(int) float64:
	printFunction(i)                       // type of i is func(int) float64
case bool, string:
	printString("type is bool or string")  // type of i is type of x (interface{})
default:
	printString("don't know the type")     // type of i is type of 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")
	}
}

型スイッチのガードの前には、ガードが評価される前に実行される単純な文を置くことができます。

fallthrough 文は、型スイッチでは許可されません。

For文

ForClauseを持つ "for" 文(A "for" statement)は、その条件によっても制御されますが、それに加えて、代入やインクリメント、デクリメントステートメントのような、initおよびpostステートメントを指定することができます。init文は、短い変数宣言であっても構いませんが、post文はそうではありません。init文で宣言された変数は、各反復で再利用されます[12]

構文
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() } と等しい

range 節のある文の場合

range節を持つfor文は、配列、スライス、文字列、マップのすべてのエントリ、またはチャネルで受信した値を繰り返し実行します。 各エントリでは、対応するイテレーション変数があればそれにイテレーション値を割り当て、ブロックを実行します。

構文
RangeClause = [ ExpressionList "=" | IdentifierList ":=" ] "range" Expression ;

range 句の右側の式を範囲式(range expression)と呼び、配列、配列へのポインタ、スライス、文字列、マップ、受信操作を許可するチャンネルなどがあります。 代入の場合と同様に、左側のオペランドが存在する場合は、アドレス可能な式またはマップのインデックス式でなければならず、これらは反復変数を表します。 範囲式がチャネルの場合、最大で1つの反復変数が許可されますが、そうでない場合は2つまでです。 最後の反復変数が空白の識別子である場合、範囲式は、その識別子を含まない同じ節と同等である。

範囲式xは、ループを開始する前に1回評価されるが、1つの例外がある。最大で1つの反復変数が存在し、len(x)が一定の場合、範囲式は評価されない。

左側の関数呼び出しは、反復ごとに1回評価されます。各反復において、それぞれの反復変数が存在する場合、反復値は以下のように生成されます。

Range expressionType1st value2nd value
array or slice a[n]E, *[n]E, or []Eindex i inta[i] E
string sstring typeindex i intsee below rune
map mmap[K]Vkey k Km[k] V
channel cchan E, <-chan Eelement e E
  1. 配列、配列へのポインタ、またはスライスの値aに対して、インデックスの反復値は、要素のインデックス0から始まる昇順で生成されます。最大で1つの反復変数が存在する場合、レンジループは0からlen(a)-1までの反復値を生成し、配列やスライス自体へのインデックスは作成しません。nilのスライスの場合、反復回数は0です。
  2. 文字列値の場合、range 節は、バイトインデックス0から始まる文字列内のUnicodeコードポイントを反復します。連続した反復では、インデックス値は、文字列内の連続したUTF-8エンコードされたコードポイントの最初のバイトのインデックスとなり、ルーン型の2番目の値は、対応するコードポイントの値となります。反復処理で無効なUTF-8シーケンスに遭遇した場合、2番目の値は0xFFFD(Unicode置換文字)となり、次の反復処理では文字列内の1バイトを進めることになります。
  3. マップ上の反復順序は指定されておらず、ある反復から次の反復まで同じであることは保証されていません。まだ到達していないマップエントリが反復中に削除された場合、対応する反復の値は生成されません。反復処理中にマップエントリが作成された場合、そのエントリは反復処理中に生成されることもあれば、スキップされることもあります。選択肢は、作成されたエントリごとに、また、ある反復から次の反復へと変化する可能性があります。マップがnilの場合、反復回数は0です。
  4. チャネルの場合、生成される反復値は、チャネルが閉じられるまで、チャネル上で送信される連続した値です。チャネルがnilの場合、レンジ表現は永遠にブロックされます。

イテレーション値は、代入文のようにそれぞれのイテレーション変数に割り当てられる。

反復変数は、短い変数宣言(:=)の形式を用いて、range節 で宣言することができる。 この場合、反復変数の型は、それぞれの反復値の型に設定され、その範囲はfor文のブロックとなり[13]、各反復で再利用される。for文の外で反復変数を宣言した場合、実行後の値は最後の反復の値になります。

var testdata *struct {
	a *[7]int
}
for i, _ := range testdata.a {
	// testdata.a is never evaluated; len(testdata.a) is constant
	// i ranges from 0 to 6
	f(i)
}

var a [10]string
for i, s := range a {
	// type of i is int
	// type of s is string
	// s == a[i]
	g(i, s)
}

var key string
var val interface{}  // element type of m is assignable to 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 == last map key encountered in iteration
// val == map[key]

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

// empty a channel
for range ch {}

Go文

go 文は、関数呼び出しの実行を、同じアドレス空間内の独立した同時制御スレッド(independent concurrent thread of control)goroutine; ゴルーチン)として開始します[14]

構文
GoStmt = "go" Expression ;

式は、関数またはメソッドの呼び出しでなければならず、括弧でくくることはできません。組み込み関数の呼び出しは式文と同様に制限されます。

関数の値と引数は呼び出し元のgoroutineで通常通り評価されますが、通常の呼び出しとは異なり、プログラムの実行は呼び出された関数が完了するのを待ちません。 代わりに、その関数は新しいgoroutineで独立して実行を開始します。関数が終了すると、そのgoroutineも終了します。 関数に戻り値がある場合は、関数の完了時に破棄されます。

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

Select文

"select" 文("Select" statements)は、一連の可能な送信または受信操作のうち、どの操作を行うかを選択します。Switch文と似ていますが、ケースがすべて通信操作になっています[15]

構文
SelectStmt = "select" "{" { CommClause } "}" ;
CommClause = CommCase ":" StatementList ;
CommCase   = "case" ( SendStmt | RecvStmt ) | "default" ;
RecvStmt   = [ ExpressionList "=" | IdentifierList ":=" ] RecvExpr ; 
RecvExpr   = Expression ;

RecvStmt を持つケースでは、RecvExpr の結果を 1 つまたは 2 つの変数に割り当てることができ、これらの変数は短い変数宣言を使用して宣言することができます。 RecvExpr は、(括弧で囲まれた)受信操作でなければなりません。 デフォルトのケースは最大1つで、ケースのリストのどこにでも現れる可能性があります。

select文の実行は、いくつかのステップで進みます。

  1. 文内のすべてのケースについて、受信操作のチャンネルオペランドとsend文のチャンネルと右辺の式は、select文の入力時にソース順に正確に一度だけ評価されます。その結果、受信するチャンネルや送信するチャンネルのセットと、それに対応する送信する値が得られます。この評価における副作用は、どの通信操作が選択されて進行するかに関係なく発生します。RecvStmtの左辺にある短い変数宣言または代入を伴う式は、まだ評価されていません。
  2. 1つ以上の通信が続行可能な場合、続行可能な1つの通信が一様な疑似ランダム選択によって選択されます。そうでない場合は、デフォルトのケースがあれば、そのケースが選択されます。デフォルトケースがない場合は、少なくとも1つの通信が続行可能になるまで、select文がブロックされる。
  3. 選択されたケースがデフォルトケースでなければ、それぞれの通信操作が実行される。
  4. 選択されたケースが短い変数宣言または代入を伴うRecvStmtの場合、左辺の式が評価され、受信した値(または複数の値)が代入されます。
  5. 選択されたケースの文リストが実行されます。

nilチャンネルでの通信は決して進行しないので、nilチャンネルのみでデフォルトケースのない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 の折りたたみもしない
	case c <- 1:
	}
}
 
select {}  // 永遠にブロック

Return文

関数Fの Return文(A "return" statement)は、Fの実行を終了させ、オプションで1つまたは複数の結果値を提供します。Fによって延期されたすべての関数は、Fが呼び出し元に戻る前に実行されます[16]

構文
ReturnStmt = "return" [ ExpressionList ] ;

結果型のない関数では、"return "文で結果値を指定してはいけません。

func noResult() {
	return 
}

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

  1. 返り値は、Return文の中で明示的に指定することができます。各式は単値で、関数の結果型の対応する要素に割り当て可能でなければなりません。
func simpleF() int {
	return 2
}

func complexF1() (re float64, im float64) {
	return -7.0, -4.0
}
  1. return」文の式リストは、多値関数の1回の呼び出しである場合があります。これは、関数から返された各値が、それぞれの値の型を持つ一時的な変数に割り当てられた後、これらの変数をリストアップした Return文が続いたような効果があり、この時点で、前のケースのルールが適用されます。
func complexF2() (re float64, im float64) {
	return complexF1()
}
  1. 関数の結果型で結果パラメータの名前が指定されている場合、式リストは空になることがあります。結果パラメータは通常のローカル変数として機能し、関数は必要に応じてそれらに値を割り当てることができます。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  // invalid return statement: err is shadowed
	}
	return 
}

Break文

Break文(A "break" statement)は、同じ関数内の最も内側にある "for"、"switch"、"select" 文の実行を終了させます[17]

構文
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文(A "continue" statement)は、最も内側にある "for" ループの次の繰り返しを、その次の文から開始します。"for" ループは同じ関数内になければなりません[18]

構文
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文(A "goto" statement)は、同じ関数内の対応するラベルを持つステートメントに制御を移します[19]

構文
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文(A "fallthrough" statement)は、式スイッチ文の次のcase節の最初のステートメントに制御を移します。これは、このような節の最後の空でない文としてのみ使用できます[20]

構文
FallthroughStmt = "fallthrough" ;

Defer文

Defer文(A "defer" statement)は、周囲の関数がリターンする瞬間まで実行が延期される関数を呼び出します[21]。 これは、周囲の関数がreturn文を実行したか、関数本体が終了したか、対応するゴルーチンがパニックに陥ったためです。

構文
DeferStmt = "defer" Expression ;

式は、関数またはメソッドの呼び出しでなければならず、括弧でくくることはできません。組み込み関数の呼び出しは、式文と同様に制限されます。

defer文が実行されるたびに、関数値と呼び出しのパラメータは通常どおり評価され、新たに保存されますが、実際の関数は呼び出されません。 代わりに、遅延された関数は、周囲の関数が戻る直前に、遅延された順序とは逆の順序で呼び出されます。 つまり、周囲の関数が明示的なreturn文によって返された場合、遅延関数はreturn文によって結果パラメータが設定された後、関数が呼び出し元に戻る前に実行されます。 遅延された関数の値がnilと評価された場合、defer文が実行された時ではなく、その関数が呼び出された時に実行がパニックになります。

例えば、遅延関数が関数リテラルで、周囲の関数がリテラル内でスコープ内にある名前付きの結果パラメータを持っている場合、遅延関数は結果パラメータが返される前にアクセスして変更することができます。 延期された関数に戻り値がある場合は、関数の完了時に破棄されます(パニックの処理の項も参照してください)。

lock(l)
defer unlock(l)  // unlocking happens before surrounding function returns

// prints 3 2 1 0 before surrounding function returns
for i := 0; i <= 3; i++ {
	defer fmt.Print(i)
}

// f returns 42
func f() (result int) {
	defer func() {
		// result is accessed after it was set to 6 by the return statement
		result *= 7
	}()
	return 6
}

脚註

  1. “Statements¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
  2. “Terminating statements¶”. The Go Programming Language Specification. Github Go Page. (Sep 16, 2021).
  3. “Empty_statements¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
  4. “Labeled_statements¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
  5. “Expression statements¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
  6. パッケージ unsafe の関数は組込み関数ではありませんが、式分の文脈での使用は許されません(他の言語の感覚だとサブルーチンやプロシージャが近いです)。
  7. “Send statements¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
  8. “IncDec statements¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
  9. “Assignments¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
  10. “If statements¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
  11. “Switch statements¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
  12. “For statements¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
  13. for文を抜けると、短い変数宣言で宣言された反復変数は参照できません。
  14. “Go statements¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
  15. “Select statements¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
  16. “Return statements¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
  17. “Break statements¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
  18. “Continue statements¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
  19. “Goto statements¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
  20. “Fallthrough statements¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
  21. “Defer statements¶”. The Go Programming Language Specification. The Go website. (Jul 26, 2021).
カテゴリ:Book:Go