Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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言語の仕様書における panicrecover の説明を書き直すことを目的としています。以前の記述が誤解を招き、不正確であったため、より明確で正確な説明に更新されました。この変更は、Go言語のIssue #4774を修正するものです。

変更の背景

Go言語における panicrecover は、プログラムの異常終了(パニック)を処理し、そのパニックから回復するためのメカニズムです。しかし、その動作、特に defer ステートメントとの相互作用や、recovernil を返す条件については、しばしば誤解が生じやすい部分でした。

このコミットが行われた背景には、Go言語のIssue #4774が存在します。このIssueでは、Go言語の仕様書における panicrecover の説明が不正確であり、開発者がこれらの機能の動作を誤解する原因となっていることが指摘されていました。特に、panic が発生した際の関数の実行フロー、defer 関数の実行順序、そして recover がパニックを停止させる条件に関する記述が不明瞭であったため、より正確で詳細な説明が求められていました。

この修正は、Go言語の公式ドキュメントの正確性を向上させ、開発者が panicrecover をより適切に理解し、利用できるようにすることを目的としています。

前提知識の解説

Go言語におけるエラーハンドリングとパニック/回復

Go言語は、エラーハンドリングに error インターフェースを用いた明示的なエラー返却を推奨しています。しかし、回復不可能なエラーや、プログラムの続行が不可能となるような致命的な状況では、panicrecover というメカニズムが提供されています。

  • panic: panic は、現在のゴルーチン(軽量スレッド)の通常の実行フローを中断させる組み込み関数です。panic が呼び出されると、現在の関数の実行が直ちに停止し、その関数に遅延(defer)された関数が実行されます。その後、パニックは呼び出し元に伝播し、呼び出し元の関数も同様に停止し、遅延された関数が実行されます。このプロセスは、ゴルーチンのスタックを遡って、すべての関数が終了するまで続きます。最終的に、ゴルーチン内のすべての関数が終了すると、プログラム全体が終了し、パニックの情報(panic に渡された引数)が報告されます。この一連の終了シーケンスを「パニック(panicking)」と呼びます。

  • recover: recover は、パニック状態のゴルーチンの動作を管理するための組み込み関数です。recover は、defer された関数内でのみ意味を持ちます。defer された関数内で recover が呼び出されると、パニックシーケンスを停止させ、通常の実行を回復させることができます。recover は、panic に渡された値を返します。もし recoverdefer された関数以外で呼び出された場合、またはゴルーチンがパニック状態でない場合、あるいは panicnil が渡された場合、recovernil を返します。

defer ステートメント

defer ステートメントは、そのステートメントを含む関数がリターンする直前(またはパニックが発生して関数が終了する直前)に、指定された関数呼び出しを実行することを保証します。defer された関数は、LIFO(後入れ先出し)の順序で実行されます。これは、リソースの解放(ファイルクローズ、ロック解除など)や、パニックからの回復処理によく利用されます。

技術的詳細

このコミットの主要な目的は、panicrecover の動作に関する既存の仕様記述の曖昧さや不正確さを解消することです。

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 であった場合、recovernil を返す。」というものでした。

新しい記述では、recover がパニックを停止させるメカニズムと、recovernil を返す条件がより詳細かつ正確に説明されています。

新しい記述の要点は以下の通りです。

  • パニックからの回復のメカニズム: 「関数 Grecover を呼び出す関数 D を遅延させ、G が実行されている同じゴルーチン内でパニックが発生したとします。遅延された関数の実行が D に到達すると、Drecover 呼び出しの戻り値は、panic の呼び出しに渡された値になります。もし D が新しい panic を開始せずに正常にリターンした場合、パニックシーケンスは停止します。その場合、Gpanic の呼び出しの間に呼び出された関数の状態は破棄され、通常の実行が再開されます。D の前に G によって遅延された関数はその後実行され、G の実行は呼び出し元にリターンすることで終了します。」 この説明は、recover がパニックを停止させる具体的なタイミングと、その後の実行フロー(スタックの巻き戻しと通常の実行の再開)を明確にしています。

  • recovernil を返す条件の明確化: 以前は箇条書きではなく、文章中に記述されていましたが、新しい記述では以下の3つの条件が箇条書きで明確に示されています。

    1. panic の引数が nil であった場合。
    2. ゴルーチンがパニック状態でない場合。
    3. recover が遅延された関数によって直接呼び出されなかった場合。 特に3番目の条件は重要で、recoverdefer された関数内で直接呼び出される必要があることを強調しています。defer された関数が別の関数を呼び出し、その中で recover が呼び出されても、パニックを停止させることはできません。

これらの変更により、panicrecover の動作に関する誤解が減り、より堅牢なエラーハンドリングコードの記述に役立つことが期待されます。

コアとなるコードの変更箇所

変更は 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 がパニックを停止させるメカニズムを、具体的な関数名(GD)を用いたシナリオで詳細に説明しています。
    • パニックシーケンスが停止した後の、スタックの巻き戻しと通常の実行の再開のプロセスを明確にしています。
  • : 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>

    • recovernil を返す条件を、箇条書きで明確に列挙しています。
    • 特に「recover が遅延された関数によって直接呼び出されなかった場合」という条件を明示し、recoverdefer された関数内で直接呼び出される必要があることを強調しています。

これらの変更は、Go言語の panicrecover の動作に関する理解を深め、より正確なドキュメントを提供することを目的としています。

関連リンク

参考にした情報源リンク