[インデックス 19460] ファイルの概要
このコミットは、Go言語の仕様書(The Go Programming Language Specification)において、break
、continue
、およびgoto
ステートメントの動作が関数境界を越えて適用されないことを明確にするための変更です。特に、これらの制御フロー文が常に同じ関数内で機能することを強調し、誤解の余地をなくすことを目的としています。
コミット
- コミットハッシュ:
94849d5a78009dcaafbde2847cc0a27f53aa3723
- Author: Robert Griesemer gri@golang.org
- Date: Wed May 28 08:43:47 2014 -0700
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/94849d5a78009dcaafbde2847cc0a27f53aa3723
元コミット内容
spec: clarify that break/continue do not work across function boundaries
Also made it extra clear for goto statements (even though label scopes
are already limited to the function defining a label).
Fixes #8040.
LGTM=r, rsc
R=r, rsc, iant, ken
CC=golang-codereviews
https://golang.org/cl/99550043
変更の背景
Go言語の初期の仕様書では、break
、continue
、goto
といった制御フロー文の適用範囲について、関数境界との関係が明示的に記述されていない部分がありました。これにより、これらのステートメントが関数スコープを超えて影響を及ぼす可能性があるという誤解が生じる可能性がありました。
特に、break
やcontinue
はループやswitch
、select
ステートメントの実行を制御しますが、これらがネストされた関数呼び出しの内部から外側の関数にあるループなどを制御できるのか、という疑問が生じることがありました。同様に、goto
ステートメントもラベルへのジャンプを伴いますが、そのラベルが別の関数内に存在する場合にどうなるのか、という点が不明瞭でした。
このコミットは、このような曖昧さを解消し、Go言語の設計思想(制御フローは常にローカルなスコープに限定される)を明確に反映させるために行われました。これにより、開発者がGoコードの動作をより正確に理解し、予期せぬ挙動を避けることができるようになります。Fixes #8040
という記述から、この変更が特定の報告された問題や議論に対応するものであることが示唆されますが、具体的な問題内容はコミットメッセージからは読み取れません。しかし、仕様の明確化が目的であることは明らかです。
前提知識の解説
このコミットの理解には、以下のGo言語の基本的な概念と制御フロー文に関する知識が必要です。
- 関数 (Functions): Go言語におけるコードの基本的な実行単位です。各関数は独自のスコープを持ち、その中で宣言された変数やラベルは通常、その関数内でのみ有効です。
break
ステートメント:for
ループ、switch
ステートメント、select
ステートメントの実行を即座に終了させ、それらのステートメントの直後のコードに制御を移します。- ラベル付き
break
を使用すると、ネストされたループやswitch
/select
の特定の外側のステートメントを終了させることができます。
continue
ステートメント:for
ループの現在のイテレーションをスキップし、次のイテレーションを開始します。- ラベル付き
continue
を使用すると、ネストされたループの特定の外側のループの次のイテレーションを開始できます。
goto
ステートメント:- プログラムの実行フローを、指定されたラベルが付いたステートメントに無条件にジャンプさせます。
goto
の利用は、コードの可読性を損なう可能性があるため、一般的には推奨されません。
- スコープ (Scope): プログラム内で識別子(変数、関数、ラベルなど)が参照可能である領域を指します。Go言語では、識別子のスコープは通常、それらが宣言されたブロックや関数に限定されます。
このコミットの核心は、これらの制御フロー文が「関数スコープ」という境界を越えて作用することはない、というGo言語の設計原則を仕様書に明記することにあります。つまり、ある関数内でbreak
、continue
、goto
を使用しても、それが呼び出し元の関数や別の関数内の構造に影響を与えることはありません。
技術的詳細
このコミットは、Go言語の仕様書であるdoc/go_spec.html
ファイルを直接修正することで、制御フロー文のセマンティクスを明確にしています。具体的な変更は、break
、continue
、goto
の各ステートメントの説明に「同じ関数内(within the same function)」という文言を追加することです。
break
ステートメント: 以前の記述では、break
がfor
、switch
、select
ステートメントを終了させるとだけ書かれていましたが、これに「同じ関数内にある」という条件が追加されました。これにより、関数呼び出しを挟んで外側のループなどをbreak
することはできないことが明確になります。continue
ステートメント: 同様に、continue
がfor
ループの次のイテレーションを開始する際に、「そのfor
ループが同じ関数内になければならない」という条件が追加されました。goto
ステートメント:goto
がラベルに制御を移す際に、「そのラベルが同じ関数内になければならない」という条件が追加されました。goto
のラベルスコープは元々関数内に限定されていましたが、この変更によりその点がより強調され、誤解の余地がなくなりました。
これらの変更は、Goコンパイラやランタイムの実際の挙動を変更するものではなく、あくまでGo言語の「仕様」をより正確かつ明確に記述するためのものです。Go言語の設計者は、これらの制御フロー文が関数境界を越えて作用しないことを意図しており、このコミットはその意図を公式文書に反映させたものと言えます。これにより、Go言語の学習者や開発者が、言語の挙動についてより正確な理解を持つことができるようになります。
コアとなるコードの変更箇所
変更はdoc/go_spec.html
ファイルに対して行われました。以下に、その差分を示します。
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
<!--{
"Title": "The Go Programming Language Specification",
-" Subtitle": "Version of May 22, 2014",
+" Subtitle": "Version of May 28, 2014",
"Path": "/ref/spec"
}-->
@@ -5093,7 +5093,8 @@ func f(n int) (res int, err error) {
A "break" statement terminates execution of the innermost
<a href="#For_statements">"for"</a>,
<a href="#Switch_statements">"switch"</a>, or
-<a href="#Select_statements">"select"</a> statement.
+<a href="#Select_statements">"select"</a> statement
+within the same function.
</p>
<pre class="ebnf">
@@ -5127,6 +5128,7 @@ OuterLoop:\n <p>\n A "continue" statement begins the next iteration of the\n innermost <a href="#For_statements">"for" loop</a> at its post statement.\n+The "for" loop must be within the same function.\n </p>\n \n <pre class="ebnf">\n@@ -5154,7 +5156,8 @@ RowLoop:\n <h3 id=\"Goto_statements\">Goto statements</h3>\n \n <p>\n-A "goto" statement transfers control to the statement with the corresponding label.\n+A "goto" statement transfers control to the statement with the corresponding label\n+within the same function.\n </p>\n \n <pre class="ebnf">\n```
## コアとなるコードの解説
上記の差分は、Go言語の仕様書における以下の3つのセクションに修正を加えています。
1. **`break` ステートメントのセクション**:
* 変更前: `A "break" statement terminates execution of the innermost "for", "switch", or "select" statement.`
* 変更後: `A "break" statement terminates execution of the innermost "for", "switch", or "select" statement within the same function.`
* 解説: `break`が適用される`for`、`switch`、`select`ステートメントが、`break`ステートメントと同じ関数内になければならないことを明示しています。これにより、関数呼び出しを介して外側の制御構造を`break`できないことが明確になります。
2. **`continue` ステートメントのセクション**:
* 変更前: `A "continue" statement begins the next iteration of the innermost "for" loop at its post statement.`
* 変更後: `A "continue" statement begins the next iteration of the innermost "for" loop at its post statement. The "for" loop must be within the same function.`
* 解説: `continue`が適用される`for`ループが、`continue`ステートメントと同じ関数内になければならないことを明確にしています。
3. **`goto` ステートメントのセクション**:
* 変更前: `A "goto" statement transfers control to the statement with the corresponding label.`
* 変更後: `A "goto" statement transfers control to the statement with the corresponding label within the same function.`
* 解説: `goto`がジャンプする先のラベルが、`goto`ステートメントと同じ関数内になければならないことを明示しています。`goto`のラベルスコープは元々関数内に限定されるため、これは既存の挙動の再確認と強調になります。
これらの変更は、Go言語の制御フローのセマンティクスに関する曖昧さを排除し、仕様書をより正確で理解しやすいものにすることを目的としています。
## 関連リンク
* GitHubコミットページ: [https://github.com/golang/go/commit/94849d5a78009dcaafbde2847cc0a27f53aa3723](https://github.com/golang/go/commit/94849d5a78009dcaafbde2847cc0a27f53aa3723)
* Go Code Review (Gerrit) リンク: `https://golang.org/cl/99550043` (これは古いGerritのURL形式であり、現在はGitHubのプルリクエストに統合されていますが、コミットメッセージに記載されているため含めます。)
* Go Issue #8040: (直接的なGitHub Issueのリンクは見つかりませんでしたが、コミットメッセージに記載されています。)
## 参考にした情報源リンク
* The Go Programming Language Specification: [https://go.dev/ref/spec](https://go.dev/ref/spec) (Go言語の公式仕様書)
* Go言語の公式ドキュメント (break, continue, gotoに関するセクション)