[インデックス 14277] ファイルの概要
このコミットは、Go言語の仕様書(doc/go_spec.html
)におけるreturn
ステートメント、defer
ステートメント、およびpanic
の振る舞いに関する記述を明確化することを目的としています。特に、これらの機能がどのように相互作用し、いつ遅延関数(deferred functions)が実行されるのかについて、より詳細で正確な説明が追加されています。
コミット
commit 1e8e14c901cebde0550c4fe0c1a77b3902d6080d
Author: Robert Griesemer <gri@golang.org>
Date: Thu Nov 1 10:13:48 2012 -0700
spec: clarify returns, defer statements, and panics
This is an attempt at making the interaction between
these three constructs clearer. Specifically:
- return statements terminate a function, execute deferred
functions, return to the caller, and then execution
continues after the call
- panic calls terminate a function, execute deferred
functions, return to the caller, and then re-panic
- deferred functions are executed before a function _returns_
to its caller
The hope is that with this change it becomes clear when a
deferred function is executed (when a function returns),
and when it is not (when a program exits).
R=r, rsc, iant, ken, iant
CC=golang-dev
https://golang.org/cl/6736071
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1e8e14c901cebde0550c4fe0c1a77b3902d6080d
元コミット内容
spec: clarify returns, defer statements, and panics
This is an attempt at making the interaction between
these three constructs clearer. Specifically:
- return statements terminate a function, execute deferred
functions, return to the caller, and then execution
continues after the call
- panic calls terminate a function, execute deferred
functions, return to the caller, and then re-panic
- deferred functions are executed before a function _returns_
to its caller
The hope is that with this change it becomes clear when a
deferred function is executed (when a function returns),
and when it is not (when a program exits).
変更の背景
Go言語において、return
、defer
、panic
は関数の実行フローを制御する上で非常に重要な要素です。しかし、これらの相互作用、特にdefer
がいつ実行されるのか、そしてpanic
が発生した場合にどのように振る舞うのかについては、開発者にとって混乱を招く可能性がありました。
このコミットの背景には、Go言語の仕様をより明確にし、曖昧さを排除するという目的があります。特に、以下の点について明確な定義が求められていました。
return
ステートメントとdefer
の相互作用: 関数がreturn
する際に、defer
された関数が正確にどのタイミングで実行されるのか。panic
とdefer
の相互作用:panic
が発生した場合に、defer
された関数が実行されるのか、そしてその後の実行フローはどうなるのか。- プログラム終了時の
defer
の振る舞い: 関数が正常に終了するのではなく、プログラム自体が終了する場合にdefer
が実行されるのかどうか。
これらの点を明確にすることで、開発者がGoプログラムの実行フローをより正確に予測し、堅牢なコードを書けるようにすることが、この変更の主な動機となっています。特に、リソースの解放(ファイルクローズ、ロック解除など)をdefer
で行う際に、その信頼性を保証するためには、defer
の実行タイミングに関する明確な保証が必要でした。
前提知識の解説
このコミットの変更内容を理解するためには、Go言語における以下の基本的な概念を理解しておく必要があります。
1. return
ステートメント
return
ステートメントは、関数の実行を終了し、呼び出し元に制御を戻すために使用されます。関数が値を返す場合、return
ステートメントは返す値を指定します。
例:
func add(a, b int) int {
return a + b // 関数の実行を終了し、a+b の値を返す
}
2. defer
ステートメント
defer
ステートメントは、そのステートメントを含む関数が終了する直前に、指定された関数呼び出し(遅延関数)を実行することをスケジュールします。defer
された関数は、その関数が正常に終了する場合でも、panic
によって異常終了する場合でも、必ず実行されます。複数のdefer
ステートメントがある場合、それらはLIFO(Last-In, First-Out)の順序で実行されます。
defer
の主な用途は、リソースの解放(ファイルのクローズ、データベース接続のクローズ、ミューテックスのアンロックなど)を保証することです。これにより、エラーハンドリングパスを含め、関数のどの終了パスでもリソースが適切にクリーンアップされることが保証されます。
例:
func readFile(filename string) ([]byte, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close() // 関数が終了する直前にファイルがクローズされることを保証
data, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
return data, nil
}
3. panic
とrecover
panic
は、プログラムの通常の実行フローを中断するために使用される組み込み関数です。panic
が呼び出されると、現在の関数の実行は直ちに停止し、defer
された関数がLIFO順に実行されます。その後、制御は呼び出し元に渡され、呼び出し元でも同様にpanic
が伝播し、defer
された関数が実行されます。このプロセスは、現在のゴルーチン内のすべての関数が終了するまで続きます。最終的に、panic
がrecover
されなければ、プログラムはクラッシュします。
recover
は、panic
が発生したゴルーチン内でdefer
された関数の中からのみ呼び出すことができる組み込み関数です。recover
が呼び出されると、panic
の状態を捕捉し、通常の実行フローを再開させることができます。これにより、プログラムのクラッシュを防ぎ、エラーを適切に処理することが可能になります。
例:
func mayPanic() {
panic("something went wrong!")
}
func protect() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
mayPanic()
fmt.Println("This line will not be executed if mayPanic panics.")
}
これらの概念の相互作用、特にreturn
とpanic
がdefer
の実行にどのように影響するかについて、このコミットは仕様をより厳密に定義しています。
技術的詳細
このコミットは、Go言語の仕様書(doc/go_spec.html
)を更新し、return
ステートメント、defer
ステートメント、およびpanic
の相互作用に関する記述を明確にしています。主な変更点は以下の通りです。
1. return
ステートメントの明確化
変更前は、「return
ステートメントは、含まれる関数の実行を終了し、オプションで呼び出し元に結果値を提供する」とだけ記述されていました。
変更後には、以下の重要な点が追加されました。
F
の実行終了: 関数F
内のreturn
ステートメントは、F
の実行を終了します。- 遅延関数の実行:
F
によってdefer
された関数は、F
が呼び出し元に戻る前に実行されます。 - 結果パラメータの設定:
return
ステートメントが結果を指定する場合、遅延関数が実行される前に結果パラメータが設定されます。これは、遅延関数が名前付き戻り値を変更できるGoの機能(defer
された関数が名前付き戻り値を参照・変更できる)と密接に関連しています。
これにより、return
が呼び出された後、実際に呼び出し元に制御が戻る前に、defer
されたクリーンアップ処理が確実に実行されることが保証されます。
2. defer
ステートメントの明確化
defer
ステートメントの実行タイミングについて、より詳細な条件が追加されました。
変更前は、「defer
ステートメントは、周囲の関数が戻る瞬間に実行が遅延される関数を呼び出す」とだけ記述されていました。
変更後には、遅延関数が実行される具体的なシナリオが明記されました。
return
ステートメントの実行: 周囲の関数がreturn
ステートメントを実行した場合。- 関数本体の終端への到達: 周囲の関数がその関数本体の終端に到達した場合(暗黙的な
return
)。 - ゴルーチンのパニック: 対応するゴルーチンが
panic
状態になった場合。
また、defer
された関数は、defer
ステートメントが実行された時点ではなく、周囲の関数が戻る直前に、defer
された順序とは逆のLIFO順で実行されることが改めて強調されています。
3. panic
の明確化
panic
の振る舞い、特にdefer
との相互作用について、より簡潔かつ正確な記述に修正されました。
変更前は、panic
が呼び出された際の詳細な実行フロー(F
の実行停止、defer
関数の実行、呼び出し元へのpanic
の伝播、プログラム終了)がやや冗長に記述されていました。
変更後には、以下の点が明確にされました。
F
の実行終了: 関数F
内のpanic
呼び出しは、F
の実行を終了します。- 遅延関数の実行:
F
によってdefer
された関数は、F
が呼び出し元に戻る前に実行されます。 - 呼び出し元への
panic
の伝播: 呼び出し元に対しては、F
の呼び出し自体がpanic
呼び出しのように振る舞い、自身の実行を終了し、同様に遅延関数を実行します。 - ゴルーチン全体の終了: このプロセスは、ゴルーチン内のすべての関数が逆順に実行を停止するまで続き、最終的にプログラムが終了し、エラー状態が報告されます。
この変更により、panic
が発生した場合でもdefer
された関数が確実に実行されるというGoの重要な保証が、より明確に示されています。これは、リソースのクリーンアップがpanic
時にも行われることを意味し、堅牢なエラーハンドリングの基盤となります。
これらの変更は、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 October 31, 2012",
+ "Subtitle": "Version of November 1, 2012",
"Path": "/ref/spec"
}-->
@@ -4540,8 +4540,10 @@ select {} // block forever
<h3 id=\"Return_statements\">Return statements</h3>
<p>
-A \"return\" statement terminates execution of the containing function
-and optionally provides a result value or values to the caller.
+A \"return\" statement in a function <code>F</code> terminates the execution
+of <code>F</code>, and optionally provides one or more result values.
+Any functions <a href=\"#Defer_statements\">deferred</a> by <code>F</code>
+are executed before <code>F</code> returns to its caller.
</p>
<pre class=\"ebnf\">\
@@ -4611,7 +4613,10 @@ func (devnull) Write(p []byte) (n int, _ error) {
</ol>
<p>
-Regardless of how they are declared, all the result values are initialized to the zero values for their type (§<a href=\"#The_zero_value\">The zero value</a>) upon entry to the function.\n+Regardless of how they are declared, all the result values are initialized to the zero\n+values for their type (§<a href=\"#The_zero_value\">The zero value</a>) upon entry to the\n+function. A \"return\" statement that specifies results sets the result parameters before\n+any deferred functions are executed.\n </p>\n
<!--
@@ -4637,7 +4642,8 @@ BreakStmt = \"break\" [ Label ] .\n If there is a label, it must be that of an enclosing\n \"for\", \"switch\" or \"select\" statement, and that is the one whose execution\n terminates\n-(§<a href=\"#For_statements\">For statements</a>, §<a href=\"#Switch_statements\">Switch statements</a>, §<a href=\"#Select_statements\">Select statements</a>).\n+(§<a href=\"#For_statements\">For statements</a>, §<a href=\"#Switch_statements\">Switch statements</a>,\n+§<a href=\"#Select_statements\">Select statements</a>).\n </p>\n
<pre>\
@@ -4739,8 +4745,11 @@ FallthroughStmt = \"fallthrough\" .\n <h3 id=\"Defer_statements\">Defer statements</h3>\n \n <p>\n-A \"defer\" statement invokes a function whose execution is deferred to the moment\n-the surrounding function returns.\n+A \"defer\" statement invokes a function whose execution is deferred\n+to the moment the surrounding function returns, either because the\n+surrounding function executed a <a href=\"#Return_statements\">return statement</a>,\n+reached the end of its <a href=\"#Function_declarations\">function body</a>,\n+or because the corresponding goroutine is <a href=\"#Handling_panics\">panicking</a>.\n </p>\n
<pre class=\"ebnf\">\
@@ -4752,13 +4761,14 @@ The expression must be a function or method call.\n Each time the \"defer\" statement\n executes, the function value and parameters to the call are\n <a href=\"#Calls\">evaluated as usual</a>\n-and saved anew but the\n-actual function is not invoked.\n-Instead, deferred calls are executed in LIFO order\n-immediately before the surrounding function returns,\n-after the return values, if any, have been evaluated and assigned\n-to the result parameters, but before they\n-are returned to the caller. For instance, if the deferred function is\n+and saved anew but the actual function body is not executed.\n+Instead, deferred functions are executed immediately before\n+the surrounding function returns, in the reverse order\n+they were deferred.\n+</p>\n+\n+<p>\n+For instance, if the deferred function is\n a <a href=\"#Function_literals\">function literal</a> and the surrounding\n function has <a href=\"#Function_types\">named result parameters</a> that\n are in scope within the literal, the deferred function may access and modify\n@@ -5100,21 +5110,16 @@ func recover() interface{}\n </pre>\n \n <p>\n-When a function <code>F</code> calls <code>panic</code>, normal\n-execution of <code>F</code> stops immediately. Any functions whose\n-execution was <a href=\"#Defer_statements\">deferred</a> by the\n-invocation of <code>F</code> are immediately run in the usual way,\n-but with the current values of any result parameters, and then\n-<code>F</code> returns to its caller without executing the rest of\n-the function. To the caller, <code>F</code>\n-then behaves like a call to <code>panic</code>, terminating its own\n-execution and running deferred functions in the same manner.\n-This continues until all\n-functions in the goroutine have ceased execution, in reverse order.\n-At that point, the program is\n-terminated and the error condition is reported, including the value of\n-the argument to <code>panic</code>. This termination sequence is\n-called <i>panicking</i>.\n+A <code>panic</code> call in a function <code>F</code> terminates the execution\n+of <code>F</code>.\n+Any functions <a href=\"#Defer_statements\">deferred</a> by <code>F</code>\n+are executed before <code>F</code> returns to its caller. To the caller,\n+the call of <code>F</code> then behaves itself like a call to <code>panic</code>,\n+terminating its own execution and running deferred functions in the same manner.\n+This continues until all functions in the goroutine have ceased execution,\n+in reverse order. At that point, the program is terminated and the error\n+condition is reported, including the value of the argument to <code>panic</code>.\n+This termination sequence is called <i>panicking</i>.\n </p>\n
<pre>\
コアとなるコードの解説
このコミットは、Go言語の仕様書であるdoc/go_spec.html
の以下のセクションに修正を加えています。
-
ファイルのサブタイトル更新:
- "Subtitle": "Version of October 31, 2012", + "Subtitle": "Version of November 1, 2012",
これは単なる日付の更新であり、仕様内容の変更とは直接関係ありませんが、ドキュメントのバージョン管理の一環です。
-
Return statements
(Returnステートメント) セクションの変更:-
変更前:
A "return" statement terminates execution of the containing function and optionally provides a result value or values to the caller.
-
変更後:
A "return" statement in a function <code>F</code> terminates the execution of <code>F</code>, and optionally provides one or more result values. Any functions <a href="#Defer_statements">deferred</a> by <code>F</code> are executed before <code>F</code> returns to its caller.
この変更により、
return
ステートメントが単に関数を終了させるだけでなく、F
が呼び出し元に戻る前に、F
によってdefer
された関数が実行されることが明確に記述されました。これは、defer
の実行タイミングに関する重要な保証です。 -
変更前:
Regardless of how they are declared, all the result values are initialized to the zero values for their type (§<a href="#The_zero_value">The zero value</a>) upon entry to the function.
-
変更後:
Regardless of how they are declared, all the result values are initialized to the zero values for their type (§<a href="#The_zero_value">The zero value</a>) upon entry to the function. A "return" statement that specifies results sets the result parameters before any deferred functions are executed.
この追加により、結果パラメータが設定された後に
defer
された関数が実行されることが明確になりました。これは、名前付き戻り値を持つ関数でdefer
された関数がその戻り値を変更できるというGoの機能の根拠となります。つまり、return
が値を設定し、その後にdefer
が実行されるため、defer
内でその設定された値をさらに操作することが可能になります。
-
-
Break statements
(Breakステートメント) セクションの軽微な書式変更:-(§<a href="#For_statements">For statements</a>, §<a href="#Switch_statements">Switch statements</a>, §<a href="#Select_statements">Select statements</a>).\n+(§<a href="#For_statements">For statements</a>, §<a href="#Switch_statements">Switch statements</a>,\n+§<a href="#Select_statements">Select statements</a>).\n``` これは単なる改行の追加であり、意味的な変更はありません。
-
Defer statements
(Deferステートメント) セクションの変更:-
変更前:
A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns.
-
変更後:
A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a <a href="#Return_statements">return statement</a>, reached the end of its <a href="#Function_declarations">function body</a>, or because the corresponding goroutine is <a href="#Handling_panics">panicking</a>.
この変更は、
defer
された関数が実行される具体的な3つのシナリオ(return
ステートメントの実行、関数本体の終端への到達、panic
の発生)を明示することで、defer
の実行タイミングに関する曖昧さを解消しています。 -
変更前:
Each time the "defer" statement executes, the function value and parameters to the call are <a href="#Calls">evaluated as usual</a> and saved anew but the actual function is not invoked. Instead, deferred calls are executed in LIFO order immediately before the surrounding function returns, after the return values, if any, have been evaluated and assigned to the result parameters, but before they are returned to the caller. For instance, if the deferred function is
-
変更後:
Each time the "defer" statement executes, the function value and parameters to the call are <a href="#Calls">evaluated as usual</a> and saved anew but the actual function body is not executed. Instead, deferred functions are executed immediately before the surrounding function returns, in the reverse order they were deferred. </p> <p> For instance, if the deferred function is
この変更は、
defer
された関数がLIFO順で実行されること、そしてそれが「周囲の関数が戻る直前」に行われることをより簡潔に表現しています。また、HTMLの段落タグの修正も行われています。
-
-
Handling panics
(パニックの処理) セクションの変更:- 変更前:
panic
が発生した際の詳細な実行フローがやや冗長に記述されていました。 - 変更後:
この修正により、A <code>panic</code> call in a function <code>F</code> terminates the execution of <code>F</code>. Any functions <a href="#Defer_statements">deferred</a> by <code>F</code> are executed before <code>F</code> returns to its caller. To the caller, the call of <code>F</code> then behaves itself like a call to <code>panic</code>, terminating its own execution and running deferred functions in the same manner. This continues until all functions in the goroutine have ceased execution, in reverse order. At that point, the program is terminated and the error condition is reported, including the value of the argument to <code>panic</code>. This termination sequence is called <i>panicking</i>.
panic
が関数F
の実行を終了させ、defer
された関数がF
が呼び出し元に戻る前に実行されることが明確に述べられています。さらに、panic
が呼び出し元に伝播し、ゴルーチン内のすべての関数が終了するまでdefer
が実行され続けるという一連のプロセスが、より簡潔かつ正確に説明されています。これにより、panic
時にもdefer
が確実に実行されるというGoの重要な特性が強調されています。
- 変更前:
これらの変更は、Go言語の実行モデル、特にエラー処理とリソース管理の側面をより厳密に定義し、開発者がコードの振る舞いをより正確に理解できるようにすることを目的としています。
関連リンク
- Go言語の仕様書: https://go.dev/ref/spec (このコミットが変更したドキュメントの最新版)
- Go言語の
defer
に関する公式ブログ記事: https://go.dev/blog/defer-panic-and-recover (このコミットの意図をより深く理解するのに役立つ可能性があります)
参考にした情報源リンク
- Go言語の公式ドキュメント: https://go.dev/doc/
- Go言語の仕様書(コミット対象ファイル): https://go.dev/ref/spec
- Goのコードレビューシステム (Gerrit) の変更リスト: https://golang.org/cl/6736071 (コミットメッセージに記載されているリンク)
- Go言語の
defer
、panic
、recover
に関する一般的な解説記事やチュートリアル (Web検索を通じて得られた情報)- 例: A Tour of Go - Defer: https://go.dev/tour/flowcontrol/12
- 例: A Tour of Go - Panics: https://go.dev/tour/flowcontrol/13
- Go言語の
defer
、panic
、recover
の動作に関する技術ブログやStack Overflowの議論など。- これらの情報は、Goの
defer
、panic
、return
の相互作用に関する一般的な理解を深めるために参照しました。# [インデックス 14277] ファイルの概要
- これらの情報は、Goの
このコミットは、Go言語の仕様書(doc/go_spec.html
)におけるreturn
ステートメント、defer
ステートメント、およびpanic
の振る舞いに関する記述を明確化することを目的としています。特に、これらの機能がどのように相互作用し、いつ遅延関数(deferred functions)が実行されるのかについて、より詳細で正確な説明が追加されています。
コミット
commit 1e8e14c901cebde0550c4fe0c1a77b3902d6080d
Author: Robert Griesemer <gri@golang.org>
Date: Thu Nov 1 10:13:48 2012 -0700
spec: clarify returns, defer statements, and panics
This is an attempt at making the interaction between
these three constructs clearer. Specifically:
- return statements terminate a function, execute deferred
functions, return to the caller, and then execution
continues after the call
- panic calls terminate a function, execute deferred
functions, return to the caller, and then re-panic
- deferred functions are executed before a function _returns_
to its caller
The hope is that with this change it becomes clear when a
deferred function is executed (when a function returns),
and when it is not (when a program exits).
R=r, rsc, iant, ken, iant
CC=golang-dev
https://golang.org/cl/6736071
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1e8e14c901cebde0550c4fe0c1a77b3902d6080d
元コミット内容
spec: clarify returns, defer statements, and panics
This is an attempt at making the interaction between
these three constructs clearer. Specifically:
- return statements terminate a function, execute deferred
functions, return to the caller, and then execution
continues after the call
- panic calls terminate a function, execute deferred
functions, return to the caller, and then re-panic
- deferred functions are executed before a function _returns_
to its caller
The hope is that with this change it becomes clear when a
deferred function is executed (when a function returns),
and when it is not (when a program exits).
変更の背景
Go言語において、return
、defer
、panic
は関数の実行フローを制御する上で非常に重要な要素です。しかし、これらの相互作用、特にdefer
がいつ実行されるのか、そしてpanic
が発生した場合にどのように振る舞うのかについては、開発者にとって混乱を招く可能性がありました。
このコミットの背景には、Go言語の仕様をより明確にし、曖昧さを排除するという目的があります。特に、以下の点について明確な定義が求められていました。
return
ステートメントとdefer
の相互作用: 関数がreturn
する際に、defer
された関数が正確にどのタイミングで実行されるのか。panic
とdefer
の相互作用:panic
が発生した場合に、defer
された関数が実行されるのか、そしてその後の実行フローはどうなるのか。- プログラム終了時の
defer
の振る舞い: 関数が正常に終了するのではなく、プログラム自体が終了する場合にdefer
が実行されるのかどうか。
これらの点を明確にすることで、開発者がGoプログラムの実行フローをより正確に予測し、堅牢なコードを書けるようにすることが、この変更の主な動機となっています。特に、リソースの解放(ファイルクローズ、ロック解除など)をdefer
で行う際に、その信頼性を保証するためには、defer
の実行タイミングに関する明確な保証が必要でした。
前提知識の解説
このコミットの変更内容を理解するためには、Go言語における以下の基本的な概念を理解しておく必要があります。
1. return
ステートメント
return
ステートメントは、関数の実行を終了し、呼び出し元に制御を戻すために使用されます。関数が値を返す場合、return
ステートメントは返す値を指定します。
例:
func add(a, b int) int {
return a + b // 関数の実行を終了し、a+b の値を返す
}
2. defer
ステートメント
defer
ステートメントは、そのステートメントを含む関数が終了する直前に、指定された関数呼び出し(遅延関数)を実行することをスケジュールします。defer
された関数は、その関数が正常に終了する場合でも、panic
によって異常終了する場合でも、必ず実行されます。複数のdefer
ステートメントがある場合、それらはLIFO(Last-In, First-Out)の順序で実行されます。
defer
の主な用途は、リソースの解放(ファイルのクローズ、データベース接続のクローズ、ミューテックスのアンロックなど)を保証することです。これにより、エラーハンドリングパスを含め、関数のどの終了パスでもリソースが適切にクリーンアップされることが保証されます。
例:
func readFile(filename string) ([]byte, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close() // 関数が終了する直前にファイルがクローズされることを保証
data, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
return data, nil
}
3. panic
とrecover
panic
は、プログラムの通常の実行フローを中断するために使用される組み込み関数です。panic
が呼び出されると、現在の関数の実行は直ちに停止し、defer
された関数がLIFO順に実行されます。その後、制御は呼び出し元に渡され、呼び出し元でも同様にpanic
が伝播し、defer
された関数が実行されます。このプロセスは、現在のゴルーチン内のすべての関数が終了するまで続きます。最終的に、panic
がrecover
されなければ、プログラムはクラッシュします。
recover
は、panic
が発生したゴルーチン内でdefer
された関数の中からのみ呼び出すことができる組み込み関数です。recover
が呼び出されると、panic
の状態を捕捉し、通常の実行フローを再開させることができます。これにより、プログラムのクラッシュを防ぎ、エラーを適切に処理することが可能になります。
例:
func mayPanic() {
panic("something went wrong!")
}
func protect() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
mayPanic()
fmt.Println("This line will not be executed if mayPanic panics.")
}
これらの概念の相互作用、特にreturn
とpanic
がdefer
の実行にどのように影響するかについて、このコミットは仕様をより厳密に定義しています。
技術的詳細
このコミットは、Go言語の仕様書(doc/go_spec.html
)を更新し、return
ステートメント、defer
ステートメント、およびpanic
の相互作用に関する記述を明確にしています。主な変更点は以下の通りです。
1. return
ステートメントの明確化
変更前は、「return
ステートメントは、含まれる関数の実行を終了し、オプションで呼び出し元に結果値を提供する」とだけ記述されていました。
変更後には、以下の重要な点が追加されました。
F
の実行終了: 関数F
内のreturn
ステートメントは、F
の実行を終了します。- 遅延関数の実行:
F
によってdefer
された関数は、F
が呼び出し元に戻る前に実行されます。 - 結果パラメータの設定:
return
ステートメントが結果を指定する場合、遅延関数が実行される前に結果パラメータが設定されます。これは、遅延関数が名前付き戻り値を変更できるGoの機能(defer
された関数が名前付き戻り値を参照・変更できる)と密接に関連しています。
これにより、return
が呼び出された後、実際に呼び出し元に制御が戻る前に、defer
されたクリーンアップ処理が確実に実行されることが保証されます。
2. defer
ステートメントの明確化
defer
ステートメントの実行タイミングについて、より詳細な条件が追加されました。
変更前は、「defer
ステートメントは、周囲の関数が戻る瞬間に実行が遅延される関数を呼び出す」とだけ記述されていました。
変更後には、遅延関数が実行される具体的なシナリオが明記されました。
return
ステートメントの実行: 周囲の関数がreturn
ステートメントを実行した場合。- 関数本体の終端への到達: 周囲の関数がその関数本体の終端に到達した場合(暗黙的な
return
)。 - ゴルーチンのパニック: 対応するゴルーチンが
panic
状態になった場合。
また、defer
された関数は、defer
ステートメントが実行された時点ではなく、周囲の関数が戻る直前に、defer
された順序とは逆のLIFO順で実行されることが改めて強調されています。
3. panic
の明確化
panic
の振る舞い、特にdefer
との相互作用について、より簡潔かつ正確な記述に修正されました。
変更前は、panic
が呼び出された際の詳細な実行フロー(F
の実行停止、defer
関数の実行、呼び出し元へのpanic
の伝播、プログラム終了)がやや冗長に記述されていました。
変更後には、以下の点が明確にされました。
F
の実行終了: 関数F
内のpanic
呼び出しは、F
の実行を終了します。- 遅延関数の実行:
F
によってdefer
された関数は、F
が呼び出し元に戻る前に実行されます。 - 呼び出し元への
panic
の伝播: 呼び出し元に対しては、F
の呼び出し自体がpanic
呼び出しのように振る舞い、自身の実行を終了し、同様に遅延関数を実行します。 - ゴルーチン全体の終了: このプロセスは、ゴルーチン内のすべての関数が逆順に実行を停止するまで続き、最終的にプログラムが終了し、エラー状態が報告されます。
この変更により、panic
が発生した場合でもdefer
された関数が確実に実行されるというGoの重要な保証が、より明確に示されています。これは、リソースのクリーンアップがpanic
時にも行われることを意味し、堅牢なエラーハンドリングの基盤となります。
これらの変更は、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 October 31, 2012",
+ "Subtitle": "Version of November 1, 2012",
"Path": "/ref/spec"
}-->
@@ -4540,8 +4540,10 @@ select {} // block forever
<h3 id=\"Return_statements\">Return statements</h3>
<p>
-A \"return\" statement terminates execution of the containing function
-and optionally provides a result value or values to the caller.
+A \"return\" statement in a function <code>F</code> terminates the execution
+of <code>F</code>, and optionally provides one or more result values.
+Any functions <a href=\"#Defer_statements\">deferred</a> by <code>F</code>
+are executed before <code>F</code> returns to its caller.
</p>
<pre class=\"ebnf\">\
@@ -4611,7 +4613,10 @@ func (devnull) Write(p []byte) (n int, _ error) {
</ol>
<p>
-Regardless of how they are declared, all the result values are initialized to the zero values for their type (§<a href=\"#The_zero_value\">The zero value</a>) upon entry to the function.\n+Regardless of how they are declared, all the result values are initialized to the zero\n+values for their type (§<a href=\"#The_zero_value\">The zero value</a>) upon entry to the\n+function. A \"return\" statement that specifies results sets the result parameters before\n+any deferred functions are executed.\n </p>\n
<!--
@@ -4637,7 +4642,8 @@ BreakStmt = \"break\" [ Label ] .\n If there is a label, it must be that of an enclosing\n \"for\", \"switch\" or \"select\" statement, and that is the one whose execution\n terminates\n-(§<a href=\"#For_statements\">For statements</a>, §<a href=\"#Switch_statements\">Switch statements</a>, §<a href=\"#Select_statements\">Select statements</a>).\n+(§<a href=\"#For_statements\">For statements</a>, §<a href=\"#Switch_statements\">Switch statements</a>,\n+§<a href=\"#Select_statements\">Select statements</a>).\n </p>\n
<pre>\
@@ -4739,8 +4745,11 @@ FallthroughStmt = \"fallthrough\" .\n <h3 id=\"Defer_statements\">Defer statements</h3>\n \n <p>\n-A \"defer\" statement invokes a function whose execution is deferred to the moment\n-the surrounding function returns.\n+A \"defer\" statement invokes a function whose execution is deferred\n+to the moment the surrounding function returns, either because the\n+surrounding function executed a <a href=\"#Return_statements\">return statement</a>,\n+reached the end of its <a href=\"#Function_declarations\">function body</a>,\n+or because the corresponding goroutine is <a href=\"#Handling_panics\">panicking</a>.\n </p>\n
<pre class=\"ebnf\">\
@@ -4752,13 +4761,14 @@ The expression must be a function or method call.\n Each time the \"defer\" statement\n executes, the function value and parameters to the call are\n <a href=\"#Calls\">evaluated as usual</a>\n-and saved anew but the\n-actual function is not invoked.\n-Instead, deferred calls are executed in LIFO order\n-immediately before the surrounding function returns,\n-after the return values, if any, have been evaluated and assigned\n-to the result parameters, but before they\n-are returned to the caller. For instance, if the deferred function is\n+and saved anew but the actual function body is not executed.\n+Instead, deferred functions are executed immediately before\n+the surrounding function returns, in the reverse order\n+they were deferred.\n+</p>\n+\n+<p>\n+For instance, if the deferred function is\n a <a href=\"#Function_literals\">function literal</a> and the surrounding\n function has <a href=\"#Function_types\">named result parameters</a> that\n are in scope within the literal, the deferred function may access and modify\n@@ -5100,21 +5110,16 @@ func recover() interface{}\n </pre>\n \n <p>\n-When a function <code>F</code> calls <code>panic</code>, normal\n-execution of <code>F</code> stops immediately. Any functions whose\n-execution was <a href=\"#Defer_statements\">deferred</a> by the\n-invocation of <code>F</code> are immediately run in the usual way,\n-but with the current values of any result parameters, and then\n-<code>F</code> returns to its caller without executing the rest of\n-the function. To the caller, <code>F</code>\n-then behaves like a call to <code>panic</code>, terminating its own\n-execution and running deferred functions in the same manner.\n-This continues until all\n-functions in the goroutine have ceased execution, in reverse order.\n-At that point, the program is\n-terminated and the error condition is reported, including the value of\n-the argument to <code>panic</code>. This termination sequence is\n-called <i>panicking</i>.\n+A <code>panic</code> call in a function <code>F</code> terminates the execution\n+of <code>F</code>.\n+Any functions <a href=\"#Defer_statements\">deferred</a> by <code>F</code>\n+are executed before <code>F</code> returns to its caller. To the caller,\n+the call of <code>F</code> then behaves itself like a call to <code>panic</code>,\n+terminating its own execution and running deferred functions in the same manner.\n+This continues until all functions in the goroutine have ceased execution,
+in reverse order. At that point, the program is terminated and the error
+condition is reported, including the value of the argument to <code>panic</code>.
+This termination sequence is called <i>panicking</i>.\n </p>\n
<pre>\
コアとなるコードの解説
このコミットは、Go言語の仕様書であるdoc/go_spec.html
の以下のセクションに修正を加えています。
-
ファイルのサブタイトル更新:
- "Subtitle": "Version of October 31, 2012", + "Subtitle": "Version of November 1, 2012",
これは単なる日付の更新であり、仕様内容の変更とは直接関係ありませんが、ドキュメントのバージョン管理の一環です。
-
Return statements
(Returnステートメント) セクションの変更:-
変更前:
A "return" statement terminates execution of the containing function and optionally provides a result value or values to the caller.
-
変更後:
A "return" statement in a function <code>F</code> terminates the execution of <code>F</code>, and optionally provides one or more result values. Any functions <a href="#Defer_statements">deferred</a> by <code>F</code> are executed before <code>F</code> returns to its caller.
この変更により、
return
ステートメントが単に関数を終了させるだけでなく、F
が呼び出し元に戻る前に、F
によってdefer
された関数が実行されることが明確に記述されました。これは、defer
の実行タイミングに関する重要な保証です。 -
変更前:
Regardless of how they are declared, all the result values are initialized to the zero values for their type (§<a href="#The_zero_value">The zero value</a>) upon entry to the function.
-
変更後:
Regardless of how they are declared, all the result values are initialized to the zero values for their type (§<a href="#The_zero_value">The zero value</a>) upon entry to the function. A "return" statement that specifies results sets the result parameters before any deferred functions are executed.
この追加により、結果パラメータが設定された後に
defer
された関数が実行されることが明確になりました。これは、名前付き戻り値を持つ関数でdefer
された関数がその戻り値を変更できるというGoの機能の根拠となります。つまり、return
が値を設定し、その後にdefer
が実行されるため、defer
内でその設定された値をさらに操作することが可能になります。
-
-
Break statements
(Breakステートメント) セクションの軽微な書式変更:-(§<a href="#For_statements">For statements</a>, §<a href="#Switch_statements">Switch statements</a>, §<a href="#Select_statements">Select statements</a>).\n+(§<a href="#For_statements">For statements</a>, §<a href="#Switch_statements">Switch statements</a>,\n+§<a href="#Select_statements">Select statements</a>).\n``` これは単なる改行の追加であり、意味的な変更はありません。
-
Defer statements
(Deferステートメント) セクションの変更:-
変更前:
A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns.
-
変更後:
A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a <a href="#Return_statements">return statement</a>, reached the end of its <a href="#Function_declarations">function body</a>, or because the corresponding goroutine is <a href="#Handling_panics">panicking</a>.
この変更は、
defer
された関数が実行される具体的な3つのシナリオ(return
ステートメントの実行、関数本体の終端への到達、panic
の発生)を明示することで、defer
の実行タイミングに関する曖昧さを解消しています。 -
変更前:
Each time the "defer" statement executes, the function value and parameters to the call are <a href="#Calls">evaluated as usual</a> and saved anew but the actual function is not invoked. Instead, deferred calls are executed in LIFO order immediately before the surrounding function returns, after the return values, if any, have been evaluated and assigned to the result parameters, but before they are returned to the caller. For instance, if the deferred function is
-
変更後:
Each time the "defer" statement executes, the function value and parameters to the call are <a href="#Calls">evaluated as usual</a> and saved anew but the actual function body is not executed. Instead, deferred functions are executed immediately before the surrounding function returns, in the reverse order they were deferred. </p> <p> For instance, if the deferred function is
この変更は、
defer
された関数がLIFO順で実行されること、そしてそれが「周囲の関数が戻る直前」に行われることをより簡潔に表現しています。また、HTMLの段落タグの修正も行われています。
-
-
Handling panics
(パニックの処理) セクションの変更:- 変更前:
panic
が発生した際の詳細な実行フローがやや冗長に記述されていました。 - 変更後:
この修正により、A <code>panic</code> call in a function <code>F</code> terminates the execution of <code>F</code>. Any functions <a href="#Defer_statements">deferred</a> by <code>F</code> are executed before <code>F</code> returns to its caller. To the caller, the call of <code>F</code> then behaves itself like a call to <code>panic</code>, terminating its own execution and running deferred functions in the same manner. This continues until all functions in the goroutine have ceased execution, in reverse order. At that point, the program is terminated and the error condition is reported, including the value of the argument to <code>panic</code>. This termination sequence is called <i>panicking</i>.
panic
が関数F
の実行を終了させ、defer
された関数がF
が呼び出し元に戻る前に実行されることが明確に述べられています。さらに、panic
が呼び出し元に伝播し、ゴルーチン内のすべての関数が終了するまでdefer
が実行され続けるという一連のプロセスが、より簡潔かつ正確に説明されています。これにより、panic
時にもdefer
が確実に実行されるというGoの重要な特性が強調されています。
- 変更前:
これらの変更は、Go言語の実行モデル、特にエラー処理とリソース管理の側面をより厳密に定義し、開発者がコードの振る舞いをより正確に理解できるようにすることを目的としています。
関連リンク
- Go言語の仕様書: https://go.dev/ref/spec (このコミットが変更したドキュメントの最新版)
- Go言語の
defer
に関する公式ブログ記事: https://go.dev/blog/defer-panic-and-recover (このコミットの意図をより深く理解するのに役立つ可能性があります)
参考にした情報源リンク
- Go言語の公式ドキュメント: https://go.dev/doc/
- Go言語の仕様書(コミット対象ファイル): https://go.dev/ref/spec
- Goのコードレビューシステム (Gerrit) の変更リスト: https://golang.org/cl/6736071 (コミットメッセージに記載されているリンク)
- Go言語の
defer
、panic
、recover
に関する一般的な解説記事やチュートリアル (Web検索を通じて得られた情報)- 例: A Tour of Go - Defer: https://go.dev/tour/flowcontrol/12
- 例: A Tour of Go - Panics: https://go.dev/tour/flowcontrol/13
- Go言語の
defer
、panic
、recover
の動作に関する技術ブログやStack Overflowの議論など。- これらの情報は、Goの
defer
、panic
、return
の相互作用に関する一般的な理解を深めるために参照しました。
- これらの情報は、Goの