[インデックス 15732] ファイルの概要
このコミットは、Go言語の公式仕様書である doc/go_spec.html
ファイルに対して行われた変更を記録しています。具体的には、Go言語における panic
および recover
関数の動作に関する記述が書き直され、以前の記述が誤解を招くものであったり、不正確であった点が修正されています。
コミット
commit c34050fd535113b6e1ed6fc00c9228bbd7e112db
Author: Rob Pike <r@golang.org>
Date: Tue Mar 12 14:28:16 2013 -0700
spec: rewrite the description of panic and recover.
The old description was misleading and inaccurate.
Fixes #4774.
R=iant, rsc, gri
CC=golang-dev
https://golang.org/cl/7761044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c34050fd535113b6e1ed6fc00c9228bbd7e112db
元コミット内容
このコミットは、Go言語の仕様書における panic
と recover
の説明を書き直すことを目的としています。以前の記述が誤解を招き、不正確であったため、より明確で正確な説明に更新されました。この変更は、Go言語のIssue #4774を修正するものです。
変更の背景
Go言語における panic
と recover
は、プログラムの異常終了(パニック)を処理し、そのパニックから回復するためのメカニズムです。しかし、その動作、特に defer
ステートメントとの相互作用や、recover
が nil
を返す条件については、しばしば誤解が生じやすい部分でした。
このコミットが行われた背景には、Go言語のIssue #4774が存在します。このIssueでは、Go言語の仕様書における panic
と recover
の説明が不正確であり、開発者がこれらの機能の動作を誤解する原因となっていることが指摘されていました。特に、panic
が発生した際の関数の実行フロー、defer
関数の実行順序、そして recover
がパニックを停止させる条件に関する記述が不明瞭であったため、より正確で詳細な説明が求められていました。
この修正は、Go言語の公式ドキュメントの正確性を向上させ、開発者が panic
と recover
をより適切に理解し、利用できるようにすることを目的としています。
前提知識の解説
Go言語におけるエラーハンドリングとパニック/回復
Go言語は、エラーハンドリングに error
インターフェースを用いた明示的なエラー返却を推奨しています。しかし、回復不可能なエラーや、プログラムの続行が不可能となるような致命的な状況では、panic
と recover
というメカニズムが提供されています。
-
panic
:panic
は、現在のゴルーチン(軽量スレッド)の通常の実行フローを中断させる組み込み関数です。panic
が呼び出されると、現在の関数の実行が直ちに停止し、その関数に遅延(defer
)された関数が実行されます。その後、パニックは呼び出し元に伝播し、呼び出し元の関数も同様に停止し、遅延された関数が実行されます。このプロセスは、ゴルーチンのスタックを遡って、すべての関数が終了するまで続きます。最終的に、ゴルーチン内のすべての関数が終了すると、プログラム全体が終了し、パニックの情報(panic
に渡された引数)が報告されます。この一連の終了シーケンスを「パニック(panicking)」と呼びます。 -
recover
:recover
は、パニック状態のゴルーチンの動作を管理するための組み込み関数です。recover
は、defer
された関数内でのみ意味を持ちます。defer
された関数内でrecover
が呼び出されると、パニックシーケンスを停止させ、通常の実行を回復させることができます。recover
は、panic
に渡された値を返します。もしrecover
がdefer
された関数以外で呼び出された場合、またはゴルーチンがパニック状態でない場合、あるいはpanic
にnil
が渡された場合、recover
はnil
を返します。
defer
ステートメント
defer
ステートメントは、そのステートメントを含む関数がリターンする直前(またはパニックが発生して関数が終了する直前)に、指定された関数呼び出しを実行することを保証します。defer
された関数は、LIFO(後入れ先出し)の順序で実行されます。これは、リソースの解放(ファイルクローズ、ロック解除など)や、パニックからの回復処理によく利用されます。
技術的詳細
このコミットの主要な目的は、panic
と recover
の動作に関する既存の仕様記述の曖昧さや不正確さを解消することです。
panic
の記述の変更点
以前の panic
の記述は、「関数 F
での panic
呼び出しは F
の実行を終了させる。F
によって遅延された関数は、F
が呼び出し元に戻る前に実行される。呼び出し元にとって、F
の呼び出しはそれ自体が panic
の呼び出しのように振る舞い、自身の実行を終了させ、同様に遅延された関数を実行する。これは、ゴルーチン内のすべての関数が逆順に実行を停止するまで続く。」というものでした。
新しい記述では、このプロセスがより明確に説明されています。
「関数 F
の実行中に、明示的な panic
の呼び出し、または実行時パニックが発生すると、F
の実行が終了します。F
によって遅延された関数は、通常通り実行されます。次に、F
の呼び出し元によって実行される遅延関数が実行され、実行中のゴルーチン内のトップレベル関数によって遅延された関数まで、スタックを遡って同様に続きます。その時点で、プログラムは終了し、panic
の引数の値を含むエラーが報告されます。この終了シーケンスは『パニック(panicking)』と呼ばれます。」
主な変更点は以下の通りです。
- 「
panic
呼び出し」だけでなく、「実行時パニック」もpanic
シーケンスを開始する要因として明示されました。 defer
関数の実行が「F
が呼び出し元に戻る前に」という曖昧な表現から、「通常通り実行される」と明確化されました。- パニックが呼び出し元に伝播する際に、呼び出し元の
defer
関数が実行されることがより詳細に記述されました。
recover
の記述の変更点
以前の recover
の記述は、「recover
関数は、パニック状態のゴルーチンの動作を管理することを可能にする。遅延された関数『内』で(ただし、その関数によって呼び出された関数ではない)recover
呼び出しを実行すると、通常の実行を回復させることでパニックシーケンスを停止させ、panic
の呼び出しに渡されたエラー値を取得する。もし recover
が遅延された関数の外で呼び出された場合、パニックシーケンスを停止しない。この場合、またはゴルーチンがパニック状態でない場合、または panic
に渡された引数が nil
であった場合、recover
は nil
を返す。」というものでした。
新しい記述では、recover
がパニックを停止させるメカニズムと、recover
が nil
を返す条件がより詳細かつ正確に説明されています。
新しい記述の要点は以下の通りです。
-
パニックからの回復のメカニズム: 「関数
G
がrecover
を呼び出す関数D
を遅延させ、G
が実行されている同じゴルーチン内でパニックが発生したとします。遅延された関数の実行がD
に到達すると、D
のrecover
呼び出しの戻り値は、panic
の呼び出しに渡された値になります。もしD
が新しいpanic
を開始せずに正常にリターンした場合、パニックシーケンスは停止します。その場合、G
とpanic
の呼び出しの間に呼び出された関数の状態は破棄され、通常の実行が再開されます。D
の前にG
によって遅延された関数はその後実行され、G
の実行は呼び出し元にリターンすることで終了します。」 この説明は、recover
がパニックを停止させる具体的なタイミングと、その後の実行フロー(スタックの巻き戻しと通常の実行の再開)を明確にしています。 -
recover
がnil
を返す条件の明確化: 以前は箇条書きではなく、文章中に記述されていましたが、新しい記述では以下の3つの条件が箇条書きで明確に示されています。panic
の引数がnil
であった場合。- ゴルーチンがパニック状態でない場合。
recover
が遅延された関数によって直接呼び出されなかった場合。 特に3番目の条件は重要で、recover
はdefer
された関数内で直接呼び出される必要があることを強調しています。defer
された関数が別の関数を呼び出し、その中でrecover
が呼び出されても、パニックを停止させることはできません。
これらの変更により、panic
と recover
の動作に関する誤解が減り、より堅牢なエラーハンドリングコードの記述に役立つことが期待されます。
コアとなるコードの変更箇所
変更は 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 March 11, 2013",
+" Subtitle": "Version of March 12, 2013",
"Path": "/ref/spec"
}-->
@@ -5270,14 +5270,14 @@ func recover() interface{}
</pre>
<p>
-A <code>panic</code> call in a function <code>F</code> terminates the execution
-of <code>F</code>.
+While executing a function <code>F</code>,
+an explicit call to <code>panic</code> or a <a href="#Run_time_panics">run-time panic</a>
+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
+are then executed as usual.
+Next, any deferred functions run by <code>F's</code> caller are run,
+and so on up to any deferred by the top-level function in the executing goroutine.
+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>.
</p>
@@ -5290,15 +5290,34 @@ panic(Error("cannot parse"))
<p>
The <code>recover</code> function allows a program to manage behavior
-of a panicking goroutine. Executing a <code>recover</code> call
-<i>inside</i> a deferred function (but not any function called by it) stops
-the panicking sequence by restoring normal execution, and retrieves
-the error value passed to the call of <code>panic</code>. If
-<code>recover</code> is called outside the deferred function it will
-not stop a panicking sequence. In this case, or when the goroutine
-is not panicking, or if the argument supplied to <code>panic</code>
-was <code>nil</code>, <code>recover</code> returns <code>nil</code>.
+of a panicking goroutine.
+Suppose a function <code>G</code> defers a function <code>D</code> that calls
+<code>recover</code> and a panic occurs in a function on the same goroutine in which <code>G</code>
+is executing.
+When the running of deferred functions reaches <code>D</code>,
+the return value of <code>D</code>'s call to <code>recover</code> will be the value passed to the call of <code>panic</code>.
+If <code>D</code> returns normally, without starting a new
+<code>panic</code>, the panicking sequence stops. In that case,
+the state of functions called between <code>G</code> and the call to <code>panic</code>
+is discarded, and normal execution resumes.
+Any functions deferred by <code>G</code> before <code>D</code> are then run and <code>G</code>'s
+execution terminates by returning to its caller.
+</p>
+
+<p>
+The return value of <code>recover</code> is <code>nil</code> if any of the following conditions holds:
+</p>
+<ul>
+<li>
+<code>panic</code>'s argument was <code>nil</code>;
+</li>
+<li>
+the goroutine is not panicking;
+</li>
+<li>
+<code>recover</code> was not called directly by a deferred function.
+</li>
+</ul>
<p>
The <code>protect</code> function in the example below invokes
コアとなるコードの解説
panic
の説明の変更
-
旧:
A <code>panic</code> call in a function <code>F</code> terminates the execution of <code>F</code>.
-
新:
While executing a function <code>F</code>, an explicit call to <code>panic</code> or a <a href="#Run_time_panics">run-time panic</a> terminates the execution of <code>F</code>.
panic
が明示的な呼び出しだけでなく、ランタイムパニック(例: nilポインタ参照、配列の範囲外アクセスなど)によっても発生することを明確にしています。
-
旧:
Any functions <a href="#Defer_statements">deferred</a> by <code>F</code> are executed before <code>F</code> returns to its caller.
-
新:
Any functions <a href="#Defer_statements">deferred</a> by <code>F</code> are then executed as usual.
defer
関数の実行タイミングをより自然な表現に修正しています。
-
旧:
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.
-
新:
Next, any deferred functions run by <code>F's</code> caller are run, and so on up to any deferred by the top-level function in the executing goroutine.
- パニックがスタックを遡る際の
defer
関数の実行順序と、トップレベル関数までの伝播をより詳細に説明しています。
- パニックがスタックを遡る際の
recover
の説明の変更
-
旧:
Executing a <code>recover</code> call <i>inside</i> a deferred function (but not any function called by it) stops the panicking sequence by restoring normal execution, and retrieves the error value passed to the call of <code>panic</code>.
-
新:
Suppose a function <code>G</code> defers a function <code>D</code> that calls <code>recover</code> and a panic occurs in a function on the same goroutine in which <code>G</code> is executing. When the running of deferred functions reaches <code>D</code>, the return value of <code>D</code>'s call to <code>recover</code> will be the value passed to the call of <code>panic</code>. If <code>D</code> returns normally, without starting a new <code>panic</code>, the panicking sequence stops. In that case, the state of functions called between <code>G</code> and the call to <code>panic</code> is discarded, and normal execution resumes. Any functions deferred by <code>G</code> before <code>D</code> are then run and <code>G</code>'s execution terminates by returning to its caller.
recover
がパニックを停止させるメカニズムを、具体的な関数名(G
とD
)を用いたシナリオで詳細に説明しています。- パニックシーケンスが停止した後の、スタックの巻き戻しと通常の実行の再開のプロセスを明確にしています。
-
旧:
If <code>recover</code> is called outside the deferred function it will not stop a panicking sequence. In this case, or when the goroutine is not panicking, or if the argument supplied to <code>panic</code> was <code>nil</code>, <code>recover</code> returns <code>nil</code>.
-
新:
The return value of <code>recover</code> is <code>nil</code> if any of the following conditions holds: <ul><li><code>panic</code>'s argument was <code>nil</code>;</li><li>the goroutine is not panicking;</li><li><code>recover</code> was not called directly by a deferred function.</li></ul>
recover
がnil
を返す条件を、箇条書きで明確に列挙しています。- 特に「
recover
が遅延された関数によって直接呼び出されなかった場合」という条件を明示し、recover
がdefer
された関数内で直接呼び出される必要があることを強調しています。
これらの変更は、Go言語の panic
と recover
の動作に関する理解を深め、より正確なドキュメントを提供することを目的としています。
関連リンク
- Go言語のIssue #4774: https://github.com/golang/go/issues/4774
- Gerrit Change 7761044: https://golang.org/cl/7761044
参考にした情報源リンク
- Go言語公式ドキュメント: https://golang.org/doc/
- Go言語仕様書: https://golang.org/ref/spec
- A Tour of Go - Defer, Panic, and Recover: https://tour.golang.org/flowcontrol/12
- Effective Go - Defer, Panic, and Recover: https://golang.org/doc/effective_go.html#defer
- Go by Example: Panics: https://gobyexample.com/panics
- Go by Example: Defer: https://gobyexample.com/defer
- Go by Example: Recover: https://gobyexample.com/recover