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

[インデックス 14148] ファイルの概要

このコミットは、Go言語の仕様書 (doc/go_spec.html) における defer 関数と panic の挙動に関する記述の明確化を目的としています。特に、パニック発生時に遅延実行される関数が結果パラメータにアクセスし、変更できることについて、より詳細な説明が追加されています。

コミット

commit 15970c8d6d42d0c076585bbccc16a407b8710bec
Author: Rob Pike <r@golang.org>
Date:   Tue Oct 16 11:27:20 2012 +1100

    spec: more clarification about deferred functions
    Proposed new text to make matters clearer. The existing text was
    unclear about the state of result parameters when panicking.
    
    R=golang-dev, iant
    CC=golang-dev
    https://golang.org/cl/6653047

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/15970c8d6d42d0c076585bbccc16a407b8710bec

元コミット内容

このコミットは、Go言語の仕様書 (doc/go_spec.html) を更新し、defer 関数に関する記述をより明確にすることを目的としています。既存の記述では、パニック発生時の結果パラメータの状態について不明瞭な点があったため、新しいテキストが提案され、それが適用されました。

変更の背景

Go言語には、関数の実行が終了する直前(正常終了、return、または panic のいずれの場合でも)に特定の関数を実行するための defer ステートメントがあります。また、Goには実行時エラーを通知するための panic と、そのパニックから回復するための recover というメカニズムが存在します。

このコミットが行われた2012年10月時点のGo言語の仕様書では、panic が発生した際に defer 関数がどのように振る舞うか、特に、defer 関数が呼び出し元の関数の結果パラメータ(戻り値)にアクセスしたり変更したりできるのか、という点について明確な記述が不足していました。この不明瞭さは、開発者が panicdefer を組み合わせたコードを書く際に混乱を招く可能性がありました。

具体的には、panic が発生すると、通常の関数の実行は直ちに停止し、その関数によって遅延された関数が実行されます。このとき、遅延関数が呼び出し元の関数の戻り値にアクセスできるのか、そしてその値を変更できるのか、という点が重要になります。これは、recover を用いてパニックから回復し、エラー情報を戻り値として返すようなパターンを実装する際に特に重要となります。

このコミットは、この曖昧さを解消し、defer 関数がパニック発生時にも結果パラメータにアクセスし、変更できることを明示するために行われました。

前提知識の解説

Go言語の defer ステートメント

defer ステートメントは、そのステートメントを含む関数が実行を終了する直前に、指定された関数呼び出し(遅延関数)を実行することをスケジュールします。関数の終了は、正常な return ステートメントによるもの、関数の最後まで到達したもの、または panic によるもの、のいずれの場合でも発生します。

defer の主な用途は以下の通りです。

  • リソースの解放: ファイルのクローズ、ロックの解放、データベース接続のクローズなど、確保したリソースを確実に解放するために使用されます。
  • トレース/ロギング: 関数の開始と終了をログに記録するために使用できます。
  • パニックからの回復: panic が発生した際に recover 関数と組み合わせて使用することで、プログラムのクラッシュを防ぎ、エラー処理を行うことができます。

defer 関数は、その defer ステートメントが評価された時点の引数の値で呼び出しがスケジュールされます。しかし、このコミットで明確化されたように、遅延関数が呼び出し元の関数の結果パラメータにアクセスする場合、そのアクセスは遅延関数が実際に実行される時点での結果パラメータの状態に対して行われます。

Go言語の panicrecover

  • panic: panic は、Goプログラムの通常の実行フローを中断するために使用される組み込み関数です。これは、回復不可能なエラーや予期せぬ状況が発生した場合に、プログラムをクラッシュさせるために使用されます。panic が呼び出されると、現在の関数の実行は直ちに停止し、その関数によって遅延された関数が実行されます。その後、呼び出し元にパニックが伝播し、呼び出し元の関数でも同様のプロセスが繰り返されます。このプロセスは、現在のゴルーチン内のすべての関数が終了するまで続き、最終的にプログラムが終了します。

  • recover: recover は、panic から回復するために defer 関数内で呼び出される組み込み関数です。recoverdefer 関数内で呼び出され、かつ現在のゴルーチンがパニック状態にある場合、recover はパニックに渡された引数を返します。これにより、パニックの伝播が停止し、プログラムの実行が再開されます。recover がパニック状態でないときに呼び出された場合、または defer 関数以外で呼び出された場合、nil を返します。

結果パラメータ(戻り値)

Go言語の関数は、複数の戻り値を返すことができます。これらの戻り値は「結果パラメータ」と呼ばれます。名前付きの結果パラメータを使用すると、関数内で戻り値を通常の変数として扱うことができ、return ステートメントで明示的に値を指定せずに、それらの変数の現在の値を返すことができます。

このコミットの文脈では、panic が発生した際に defer 関数が実行される時点で、呼び出し元の関数の名前付き結果パラメータがどのような状態にあるのか、そして defer 関数がそれらを変更できるのか、という点が重要になります。

技術的詳細

このコミットは、Go言語の仕様書における defer ステートメントと panic の相互作用に関する記述を、より正確かつ詳細にするものです。

変更の核心は、panic が発生した際に遅延関数が実行されるタイミングと、その際に遅延関数が呼び出し元の関数の結果パラメータにアクセスできるという点にあります。

以前の仕様では、「F の呼び出しによって遅延された関数は通常の方法で実行され、その後 F はその呼び出し元に戻る」とだけ記述されていました。この記述は、遅延関数が実行される時点での結果パラメータの状態について明確ではありませんでした。

新しい記述では、この点が明確にされています。

  1. panic 発生時の遅延関数の実行: panic が発生すると、現在の関数の通常の実行は直ちに停止します。
  2. 結果パラメータの状態: 遅延関数は「結果パラメータの現在の値と共に」実行されます。これは、panic が発生した時点での(または、panic が発生する直前までに設定された)結果パラメータの値が、遅延関数から参照可能であることを意味します。
  3. 結果パラメータの変更: 遅延関数は、これらの結果パラメータにアクセスし、変更することができます。これは、defer 関数内で recover を呼び出してパニックから回復し、その回復した情報に基づいて関数の戻り値を調整するような高度なエラーハンドリングパターンを可能にします。例えば、パニックが発生した際に、エラーメッセージを関数の戻り値として設定し、正常な戻り値として関数を終了させるといったことが可能になります。
  4. 関数の終了: 遅延関数の実行後、元の関数は残りの部分を実行することなく、その呼び出し元に戻ります。そして、呼び出し元に対しても panic が伝播します。

この変更により、deferpanicrecover を用いたエラーハンドリングのセマンティクスがより明確になり、開発者がこれらの強力な機能をより正確に理解し、利用できるようになりました。特に、recover を使用してパニックから回復し、関数の戻り値を操作するパターンが、仕様上も明確にサポートされることになります。

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

--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
 <!--{
 	"Title": "The Go Programming Language Specification",
-"	Subtitle": "Version of October 10, 2012",
+"	Subtitle": "Version of October 12, 2012",
 	"Path": "/ref/spec"
 }-->
 
@@ -4735,6 +4735,9 @@ are in scope within the literal, the deferred function may access and modify
 the result parameters before they are returned.
 If the deferred function has any return values, they are discarded when
 the function completes.
+(See also the section on <a href="#Handling_panics">handling panics</a>.)
+</p>
+
 </p>
 
 <pre>
@@ -5069,10 +5072,13 @@ func recover() interface{}
 When a function <code>F</code> calls <code>panic</code>, normal
 execution of <code>F</code> stops immediately.  Any functions whose
 execution was <a href="#Defer_statements">deferred</a> by the
-invocation of <code>F</code> are run in the usual way, and then
-<code>F</code> returns to its caller.  To the caller, <code>F</code>
+invocation of <code>F</code> are immediately run in the usual way,
+but with the current values of any result parameters, and then
+<code>F</code> returns to its caller without executing the rest of
+the function.  To the caller, <code>F</code>
 then behaves like a call to <code>panic</code>, terminating its own
-execution and running deferred functions.  This continues until all
+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

コアとなるコードの解説

このコミットは、doc/go_spec.html ファイルの2つの主要なセクションに変更を加えています。

  1. 仕様書のバージョン日付の更新:

    -	"Subtitle": "Version of October 10, 2012",
    +	"Subtitle": "Version of October 12, 2012",
    

    これは、仕様書が更新された日付を反映するための単純な変更です。

  2. defer ステートメントのセクションへの追記:

    @@ -4735,6 +4735,9 @@ are in scope within the literal, the deferred function may access and modify
     the result parameters before they are returned.
     If the deferred function has any return values, they are discarded when
     the function completes.
    +(See also the section on <a href="#Handling_panics">handling panics</a>.)
    +</p>
    +
     </p>
    

    この変更は、defer ステートメントのセクションに、パニックのハンドリングに関するセクションへの参照を追加しています。これにより、読者が deferpanic の関連性についてより深く理解するために、関連するセクションに誘導されるようになります。これは、defer がパニック処理において重要な役割を果たすことを示唆しています。

  3. panic のハンドリングセクションの明確化:

    @@ -5069,10 +5072,13 @@ func recover() interface{}
     When a function <code>F</code> calls <code>panic</code>, normal
     execution of <code>F</code> stops immediately.  Any functions whose
     execution was <a href="#Defer_statements">deferred</a> by the
    -invocation of <code>F</code> are run in the usual way, and then
    -<code>F</code> returns to its caller.  To the caller, <code>F</code>
    +invocation of <code>F</code> are immediately run in the usual way,
    +but with the current values of any result parameters, and then
    +<code>F</code> returns to its caller without executing the rest of
    +the function.  To the caller, <code>F</code>
     then behaves like a call to <code>panic</code>, terminating its own
    -execution and running deferred functions.  This continues until all
    +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
    

    これがこのコミットの最も重要な変更点です。

    • 変更前: 「F の呼び出しによって遅延された関数は通常の方法で実行され、その後 F はその呼び出し元に戻る。」
    • 変更後: 「F の呼び出しによって遅延された関数は、結果パラメータの現在の値と共に、直ちに通常の方法で実行され、その後 F は関数の残りの部分を実行することなく、その呼び出し元に戻る。」

    この変更により、以下の点が明確になります。

    • panic 発生時に遅延関数が実行される際、呼び出し元の関数の結果パラメータの現在の値が遅延関数から利用可能であること。
    • 遅延関数がこれらの結果パラメータにアクセスし、必要に応じて変更できること(これは、defer ステートメントのセクションで既に述べられている内容と整合します)。
    • panic が呼び出し元に伝播する際も、同様に遅延関数が実行されること。

    この明確化は、panicrecover を用いたエラーハンドリング、特に recover を使ってパニックから回復し、関数の戻り値を操作するようなシナリオにおいて、Go言語のセマンティクスをより正確に理解するために不可欠です。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコードリポジトリ (GitHub)
  • Go言語のコードレビューシステム (golang.org/cl)
  • コミットメッセージに記載されている情報
  • Go言語の deferpanicrecover に関する一般的な知識