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

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

このコミットは、Go言語の仕様書 doc/go_spec.html に対する更新です。主な目的は、nil ポインタの逆参照がパニックを引き起こすこと、関数呼び出しのメカニズム、および go ステートメントと defer ステートメントの動作に関する記述を明確化し、詳細化することです。これにより、Go言語の挙動に関する曖昧さを解消し、より正確なドキュメントを提供します。

コミット

commit 633a2ce096464a63d939c645f31c6bf9d245b042
Author: Rob Pike <r@golang.org>
Date:   Mon Jan 23 08:40:13 2012 -0800

    spec: function invocation, panic on *nil
    Document that indirection through a nil pointer will panic.
    Explain function invocation.
    This section will need more work, but it's a start.
    
    Fixes #1865.
    Fixes #2252.
    
    R=rsc, iant, r
    CC=golang-dev
    https://golang.org/cl/5532114

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

https://github.com/golang/go/commit/633a2ce096464a63d939c645f31c6bf9d245b042

元コミット内容

spec: function invocation, panic on *nil
Document that indirection through a nil pointer will panic.
Explain function invocation.
This section will need more work, but it's a start.

Fixes #1865.
Fixes #2252.

変更の背景

このコミットは、Go言語の仕様書におけるいくつかの重要な概念の明確化を目的としています。特に、以下の2つのGoイシューを修正しています。

  • Fixes #1865: このイシューは、nil ポインタの逆参照がパニックを引き起こすという挙動が、Go言語の仕様書に明示的に記載されていないことに関するものでした。Goでは、nil ポインタを逆参照しようとするとランタイムパニックが発生しますが、この重要な挙動が仕様書で明確に文書化されていなかったため、開発者が混乱する可能性がありました。
  • Fixes #2252: このイシューは、Go言語における関数呼び出しの評価順序と引数渡しに関する仕様が不明瞭であることに関するものでした。特に、関数値と引数の評価順序、値渡しによるパラメータの受け渡し、および戻り値の処理について、より詳細な説明が求められていました。

これらのイシューに対応するため、Go言語の設計者の一人であるRob Pike氏によって、仕様書に具体的な記述が追加されました。これにより、Go言語の挙動に関する公式なドキュメントがより正確で包括的なものとなり、開発者が言語の動作をより深く理解できるようになります。

前提知識の解説

このコミットの変更内容を理解するためには、以下のGo言語の基本的な概念を把握しておく必要があります。

  • Go言語仕様 (Go Language Specification): Go言語の公式な定義文書であり、言語の構文、セマンティクス、組み込み型、ステートメント、関数、パッケージなど、Go言語のあらゆる側面を記述しています。Go言語の挙動に関する最終的な権威となります。
  • ポインタ (Pointers): Go言語におけるポインタは、変数のメモリアドレスを保持する変数です。*T は型 T へのポインタ型を表し、*x はポインタ x が指す値(逆参照)を表します。
  • nil: Goにおける nil は、ポインタ、スライス、マップ、インターフェース、チャネル、関数などのゼロ値です。これらの型が何も指していない状態を示します。
  • パニック (Panic): Goにおけるパニックは、プログラムの異常終了を引き起こすランタイムエラーの一種です。通常、回復不可能なエラーやプログラマの論理的誤りによって発生します。パニックが発生すると、通常の実行フローは中断され、遅延関数 (deferred functions) が実行された後、プログラムが終了します。
  • 関数呼び出し (Function Invocation): Goにおける関数の実行プロセスです。関数値と引数が評価され、パラメータが関数に渡され、関数本体が実行され、戻り値が呼び出し元に返されます。
  • 評価順序 (Order of Evaluation): Go言語の式やステートメントが評価される際の順序に関する規則です。特定の操作(例:関数呼び出しの引数)は、特定の順序で評価されることが保証されています。
  • 値渡し (Pass by Value): Go言語における引数と戻り値の渡し方です。関数に渡される引数や関数から返される値は、常にその値のコピーが渡されます。ポインタを渡す場合でも、ポインタの値(メモリアドレス)のコピーが渡されるため、ポインタが指す先のデータは変更できますが、ポインタ自体を関数内で変更しても呼び出し元には影響しません。
  • ゴルーチン (Goroutine): Go言語における軽量な並行実行単位です。go キーワードを使って関数を呼び出すことで、新しいゴルーチンが作成され、その関数が並行して実行されます。ゴルーチンはOSのスレッドよりもはるかに軽量であり、数千、数万のゴルーチンを同時に実行することが可能です。
  • defer ステートメント (Defer Statement): defer キーワードに続く関数呼び出しを、それを囲む関数がリターンする直前に実行するようにスケジュールします。これは、リソースの解放(ファイルクローズ、ロック解除など)やエラーハンドリングによく使用されます。遅延関数はLIFO (Last-In, First-Out) 順で実行されます。

技術的詳細

このコミットは、doc/go_spec.html ファイルに以下の主要な変更を加えています。

  1. nil ポインタの逆参照に関するパニックの明記:

    • ポインタの逆参照 (*x) に関するセクションに、xnil の場合、*x を評価しようとするとランタイムパニックが発生するという記述が追加されました。
    • 変更前:
      For an operand <code>x</code> of pointer type <code>*T</code>, the pointer
      indirection <code>*x</code> denotes the value of type <code>T</code> pointed
      to by <code>x</code>.
      
    • 変更後:
      For an operand <code>x</code> of pointer type <code>*T</code>, the pointer
      indirection <code>*x</code> denotes the value of type <code>T</code> pointed
      to by <code>x</code>.
      If <code>x</code> is <code>nil</code>, an attempt to evaluate <code>*x</code>
      will cause a <a href="#Run_time_panics">run-time panic</a>.
      
  2. 関数呼び出しのメカニズムの詳細化:

    • 関数呼び出しに関する新しい段落が追加され、関数値と引数の評価順序、パラメータの値渡し、および戻り値の値渡しについて説明されています。
    • nil 関数値の呼び出しがランタイムパニックを引き起こすことも明記されました。
    • 追加された内容:
      <p>
      In a function call, the function value and arguments are evaluated in
      <a href="#Order_of_evaluation">the usual order</a>.
      After they are evaluated, the parameters of the call are passed by value to the function
      and the called function begins execution.
      The return parameters of the function are passed by value
      back to the calling function when the function returns.
      </p>
      
      <p>
      Calling a <code>nil</code> function value 
      causes a <a href="#Run_time_panics">run-time panic</a>.
      </p>
      
  3. go ステートメント (ゴルーチン) の詳細化:

    • go ステートメントに関する記述が拡張され、関数値とパラメータが呼び出し元のゴルーチンで評価されること、そして関数が新しいゴルーチンで独立して実行されることが明確にされました。
    • 関数が終了するとゴルーチンも終了すること、および戻り値は破棄されることも追記されました。
    • 変更前:
      The expression must be a call, and
      unlike with a regular call, program execution does not wait
      for the invoked function to complete.
      
    • 変更後:
      The expression must be a call.
      The function value and parameters are
      <a href="#Calls">evaluated as usual</a>
      in the calling goroutine, but
      unlike with a regular call, program execution does not wait
      for the invoked function to complete.
      Instead, the function begins executing independently
      in a new goroutine.
      When the function terminates, its goroutine also terminates.
      If the function has any return values, they are discarded when the
      function completes.
      
  4. defer ステートメントの詳細化:

    • defer ステートメントに関する記述が拡張され、遅延関数が実行される前に、関数値とパラメータが「通常通り評価され、新しく保存される」ことが明確にされました。
    • 遅延関数に任意の戻り値がある場合、それらは関数が完了したときに破棄されることも追記されました。
    • 変更前:
      Each time the "defer" statement
      executes, the parameters to the function call are evaluated and saved anew but the
      function is not invoked.
      Deferred function calls are executed in LIFO order
      
    • 変更後:
      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
      
      そして、
      If the deferred function has any return values, they are discarded when
      the function completes.
      

これらの変更は、Go言語の仕様書をより正確で、曖昧さのないものにし、開発者が言語の挙動をより深く理解できるようにすることを目的としています。

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

doc/go_spec.html の差分から、主要な変更箇所を抜粋します。

--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -2572,6 +2572,20 @@ var pt *Point
 pt.Scale(3.5)  // method call with receiver pt
 </pre>
 
+<p>
+In a function call, the function value and arguments are evaluated in
+<a href="#Order_of_evaluation">the usual order</a>.
+After they are evaluated, the parameters of the call are passed by value to the function
+and the called function begins execution.
+The return parameters of the function are passed by value
+back to the calling function when the function returns.
+</p>
+
+<p>
+Calling a <code>nil</code> function value 
+causes a <a href="#Run_time_panics">run-time panic</a>.
+</p>
+
 <p>
 As a special case, if the return parameters of a function or method
 <code>g</code> are equal in number and individually
@@ -3042,6 +3056,8 @@ As an exception to the addressability requirement, <code>x</code> may also be a\n For an operand <code>x</code> of pointer type <code>*T</code>, the pointer\n indirection <code>*x</code> denotes the value of type <code>T</code> pointed\n to by <code>x</code>.\n+If <code>x</code> is <code>nil</code>, an attempt to evaluate <code>*x</code>\n+will cause a <a href="#Run_time_panics">run-time panic</a>.\n </p>\n \n <pre>\n@@ -4189,9 +4205,17 @@ GoStmt = "go" Expression .\n </pre>\n \n <p>\n-The expression must be a call, and\n+The expression must be a call.\n+The function value and parameters are\n+<a href="#Calls">evaluated as usual</a>\n+in the calling goroutine, but\n unlike with a regular call, program execution does not wait\n for the invoked function to complete.\n+Instead, the function begins executing independently\n+in a new goroutine.\n+When the function terminates, its goroutine also terminates.\n+If the function has any return values, they are discarded when the\n+function completes.\n </p>\n \n <pre>\n@@ -4493,9 +4515,11 @@ DeferStmt = "defer" Expression .\n <p>\n The expression must be a function or method call.\n Each time the "defer" statement\n-executes, the parameters to the function call are evaluated and saved anew but the\n-function is not invoked.\n-Deferred function calls are executed in LIFO order\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, but before they\n are returned to the caller. For instance, if the deferred function is\n@@ -4503,6 +4527,8 @@ 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 the result parameters before they are returned.\n+If the deferred function has any return values, they are discarded when\n+the function completes.\n </p>\n \n <pre>\n```

## コアとなるコードの解説

上記の差分は、Go言語の仕様書に以下の重要な説明を追加・修正しています。

1.  **関数呼び出しの明確化**:
    *   関数呼び出しにおいて、関数値と引数が「通常の順序」で評価されることが明記されました。これは、Goの評価順序に関する既存のルールに従うことを意味します。
    *   評価後、パラメータは「値渡し」で関数に渡され、関数が実行を開始します。
    *   関数がリターンする際、戻り値も「値渡し」で呼び出し元関数に返されます。
    *   `nil` の関数値を呼び出すと「ランタイムパニック」が発生することが明確に記述されました。これは、`nil` ポインタの逆参照と同様に、不正な操作に対するGoの安全機構の一部です。

2.  **`nil` ポインタの逆参照によるパニックの明記**:
    *   ポインタの逆参照 (`*x`) のセクションに、`x` が `nil` の場合、`*x` を評価しようとすると「ランタイムパニック」が発生するという重要な警告が追加されました。これは、Goプログラムが `nil` ポインタを安全に扱わない場合に発生する典型的なエラー挙動を公式に文書化したものです。

3.  **`go` ステートメント (ゴルーチン) の詳細化**:
    *   `go` ステートメントによって起動される関数呼び出しについて、関数値とパラメータが「呼び出し元のゴルーチンで通常通り評価される」ことが強調されました。これは、ゴルーチンが起動される前に引数が評価されるという重要なセマンティクスを明確にします。
    *   通常の呼び出しとは異なり、プログラムの実行は呼び出された関数が完了するのを待たないこと、そして関数が「新しいゴルーチンで独立して実行を開始する」ことが説明されました。
    *   関数が終了すると、そのゴルーチンも終了すること、そして関数に任意の戻り値がある場合、それらは関数が完了したときに「破棄される」ことが追記されました。これは、ゴルーチンが非同期実行であり、直接的な戻り値を受け取らないという性質を明確にしています。

4.  **`defer` ステートメントの詳細化**:
    *   `defer` ステートメントが実行されるたびに、関数値とパラメータが「通常通り評価され、新しく保存される」ことが明確にされました。これは、遅延関数の引数が `defer` ステートメントが実行された時点の値をキャプチャするという重要な挙動を指します。
    *   実際の関数はすぐに呼び出されず、遅延呼び出しは「LIFO (Last-In, First-Out) 順」で実行されることが再確認されました。
    *   遅延関数に任意の戻り値がある場合、それらは関数が完了したときに「破棄される」ことが追記されました。これは、`defer` 関数が主に副作用(リソースのクリーンアップなど)のために使用され、その戻り値は通常無視されるという慣習を反映しています。

これらの変更は、Go言語のセマンティクスに関する重要な側面をより正確かつ詳細に記述することで、言語仕様の堅牢性と明確性を向上させています。

## 関連リンク

*   GitHubコミットページ: [https://github.com/golang/go/commit/633a2ce096464a63d939c645f31c6bf9d245b042](https://github.com/golang/go/commit/633a2ce096464a63d939c645f31c6bf9d245b042)
*   Go Issue #1865: [https://github.com/golang/go/issues/1865](https://github.com/golang/go/issues/1865)
*   Go Issue #2252: [https://github.com/golang/go/issues/2252](https://github.com/golang/go/issues/2252)
*   Gerrit Change-Id: [https://golang.org/cl/5532114](https://golang.org/cl/5532114)

## 参考にした情報源リンク

*   Go Language Specification: [https://go.dev/ref/spec](https://go.dev/ref/spec) (このコミットが変更を加えたドキュメントの最新版)
*   A Tour of Go - Defer: [https://go.dev/tour/flowcontrol/12](https://go.dev/tour/flowcontrol/12)
*   A Tour of Go - Goroutines: [https://go.dev/tour/concurrency/1](https://go.dev/tour/concurrency/1)
*   Effective Go - Defer: [https://go.dev/doc/effective_go#defer](https://go.dev/doc/effective_go#defer)
*   Effective Go - Goroutines: [https://go.dev/doc/effective_go#goroutines](https://go.dev/doc/effective_go#goroutines)
*   Go by Example - Panics: [https://gobyexample.com/panics](https://gobyexample.com/panics)
*   Go by Example - Pointers: [https://gobyexample.com/pointers](https://gobyexample.com/pointers)
*   Go by Example - Functions: [https://gobyexample.com/functions](https://gobyexample.com/functions)
*   Go by Example - Goroutines: [https://gobyexample.com/goroutines](https://gobyexample.com/goroutines)
*   Go by Example - Defer: [https://gobyexample.com/defer](https://gobyexample.com/defer)