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

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

このコミットは、Go言語の公式仕様書である doc/go_spec.html ファイルに対する変更です。このファイルは、Go言語の構文、セマンティクス、組み込み関数、パッケージなど、言語のあらゆる側面を定義する重要なドキュメントです。Go言語の設計思想と機能の正確な記述を提供し、コンパイラやツールの実装者、そしてGoプログラマーが言語の挙動を理解するための規範となります。

コミット

このコミットは、Go言語の仕様書から、型に基づく select ステートメントの誤った、あるいは時代遅れの例を削除し、チャネルの close および closed 関数、type switch における nil の扱い、そしてチャネルに対する range ループのサポートに関する記述を追加・修正するものです。これにより、Go言語の仕様がより正確かつ最新の状態に保たれ、開発者が言語の正しい挙動を理解できるようになります。

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

https://github.com/golang/go/commit/94b67eb8d8d85abfd1592908e397d0acb42e26036c

元コミット内容

commit 94b67eb8d85abfd1592908e397d0acb42e26036c
Author: Rob Pike <r@golang.org>
Date:   Tue Mar 24 17:40:47 2009 -0700

    delete incorrect, dreg example of select on type
    
    R=gri,rsc
    DELTA=48  (28 added, 11 deleted, 9 changed)
    OCL=26630
    CL=26701
---
 doc/go_spec.html | 55 ++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 36 insertions(+), 19 deletions(-)

diff --git a/doc/go_spec.html b/doc/go_spec.html
index 383cae100b..9ecded38d0 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1121,6 +1121,7 @@ value is made using the built-in function <code>make</code>,
 which takes the channel type and an optional capacity as arguments:
 </p>
 
+\n
 <pre>
 make(chan int, 100)
 </pre>
@@ -1132,6 +1133,15 @@ buffer is not full, sends can succeed without blocking. If the capacity is zero
 or absent, the communication succeeds only when both a sender and receiver are ready.\n </p>\n \n+<p>\n+For a channel <code>c</code>, the predefined function <code>close(c)</code>\n+marks the channel as unable to accept more\n+values through a send operation.  After any previously\n+sent values have been received, receives will return\n+the zero value for the channel\'s type.  After at least one such zero value has been\n+received, <code>closed(c)</code> returns true.\n+</p>\n+\n <h2>General properties of types and values</h2>\n \n <p>\n@@ -3258,9 +3268,15 @@ in the type guard.\n TypeSwitchStat  = \"switch\" [ [ SimpleStat ] \";\" ] TypeSwitchGuard \"{\" { TypeCaseClause } \"}\" .\n TypeSwitchGuard = identifier \":=\" Expression \".\" \"(\" \"type\" \")\" .\n TypeCaseClause  = TypeSwitchCase \":\" [ StatementList ] .\n-TypeSwitchCase  = \"case\" type | \"default\" .\n+TypeSwitchCase  = \"case\" ( type | \"nil\" ) | \"default\" .\n </pre>\n \n+<p>\n+If the interface value equals <code>nil</code>,\n+only an explict <code>nil</code> case or \"default\"\n+case will execute.\n+</p>\n+\n <p>\n Given a function <code>f</code>\n that returns a value of interface type,\n@@ -3269,6 +3285,8 @@ the following type switch:\n \n <pre>\n switch i := f().(type) {\n+case nil:\n+\tprintString(\"f() returns nil\");\n case int:\n \tprintInt(i);\t// i is an int\n case float:\n@@ -3286,7 +3304,9 @@ could be rewritten:\n \n <pre>\n v := f();\n-if i, is_int := v.(int); is_int {\n+if v == nil {\n+\tprintString(\"f() returns nil\");\n+} else if i, is_int := v.(int); is_int {\n \tprintInt(i);\t// i is an int\n } else if i, is_float := v.(float); is_float {\n \tprintFloat(i);\t// i is a float\n@@ -3379,9 +3399,10 @@ RangeClause = IdentifierList ( \"=\" | \":=\" ) \"range\" Expression .\n \n <p>\n The type of the right-hand expression in the \"range\" clause must be an array,\n-slice or map, or a pointer to an array, slice or map.\n-The slice or map must not be <code>nil</code> (TODO: really?).\n-The identifier list must contain one or two identifiers denoting the\n+slice or map, or a pointer to an array, slice or map;\n+or it may be a channel.\n+If it is an array, slice or map,\n+the identifier list must contain one or two identifiers denoting the\n iteration variables. On each iteration,\n the first variable is set to the array or slice index or\n map key, and the second variable, if present, is set to the corresponding\n@@ -3391,6 +3412,11 @@ and element, or of the map key and value respectively,\n must be assignment compatible to the iteration variables.\n </p>\n <p>\n+For channels, the identifier list must contain one identifier.\n+The iteration recieves values sent on the channel until the channel is closed;\n+it does not process the zero value sent before the channel is closed.\n+</p>\n+<p>\n The iteration variables may be declared by the \"range\" clause (\":=\"), in which\n case their scope ends at the end of the \"for\" statement (§Declarations and\n scope rules). In this case their types are set to\n@@ -3516,16 +3542,6 @@ for {  // send random sequence of bits to c\n \tcase c &lt;- 1:\n \t}\n }\n-\n-var ca chan interface {};\n-var i int;\n-var f float;\n-select {\n-case i = &lt;-ca:\n-\tprint(\"received int \", i, \" from ca\\n\");\n-case f = &lt;-ca:\n-\tprint(\"received float \", f, \" from ca\\n\");\n-}\n </pre>\n \n <font color=red>\n@@ -3726,6 +3742,8 @@ for i := 0; i &lt;= 3; i++ {\n <h2>Predeclared functions</h2>\n <ul>\n \t<li>cap\n+\t<li>close\n+\t<li>closed\n \t<li>len\n \t<li>make\n \t<li>new\n@@ -4062,11 +4080,10 @@ func generate(ch chan <- int) {\n \n // Copy the values from channel \'in\' to channel \'out\',\n // removing those divisible by \'prime\'.\n-func filter(in chan <- int, out <-chan int, prime int) {\n-\tfor {\n-\t\ti := <-in;  // Receive value of new variable \'i\' from \'in\'.\n+func filter(src chan <- int, dst <-chan int, prime int) {\n+\tfor i := range src {  // Loop over values received from \'src\'.\n \t\tif i % prime != 0 {\n-\t\t\tout <- i  // Send \'i\' to channel \'out\'.\n+\t\t\tdst <- i  // Send \'i\' to channel \'dst\'.\n \t\t}\n \t}\n }\n```

## 変更の背景

このコミットの主な背景は、Go言語の進化と仕様の明確化にあります。

1.  **誤った例の削除**: 削除された `select` ステートメントの例は、`interface{}` 型のチャネルから異なる具体的な型(`int` や `float`)を `select` で直接受け取ろうとするものでした。これはGoの `select` のセマンティクス(チャネル操作の準備完了を待つ)と型アサーションの仕組みを誤解させる可能性がありました。`select` はチャネルからの値の型に基づいて分岐するものではなく、チャネル操作(送受信)が準備できたかどうかで分岐します。`interface{}` 型のチャネルから値を受け取る場合、その値は常に `interface{}` 型であり、その後の型アサーションによって具体的な型に変換されます。この例は、Go言語の初期段階における仕様の試行錯誤や、一般的な誤解を反映していたと考えられます。正確な理解を促進するため、このような誤解を招く例は削除されるべきでした。

2.  **チャネル機能の成熟と明確化**:
    *   **`close` と `closed` 関数の導入/明確化**: チャネルのライフサイクル管理は並行処理において非常に重要です。チャネルが閉じられたことを明示的に通知し、それ以降の送信を禁止する `close` 関数と、チャネルが閉じられたかどうか、およびゼロ値が受信されたかどうかを確認する `closed` 関数(後に `ok` 戻り値として統合される)の仕様への追加は、チャネルの健全な利用パターンを確立するために不可欠でした。
    *   **`type switch` における `nil` ケースのサポート**: インターフェース型は `nil` 値を持つことができ、これはGoの型システムにおける重要な特性です。`type switch` で `nil` インターフェース値を明示的に処理できることは、より堅牢で読みやすいコードを書く上で役立ちます。これにより、`if v == nil` と `type switch` を組み合わせる必要がなくなります。
    *   **チャネルに対する `range` ループのサポート**: `for ... range` 構文は、スライス、配列、マップのイテレーションを簡潔に記述するための強力な機能です。これをチャネルにも拡張することで、チャネルからの値の受信を、チャネルが閉じられるまで自動的に反復処理できるようになり、コードの簡潔性と可読性が大幅に向上します。これは、Goの並行処理パターンにおけるイディオムとして定着しました。

これらの変更は、Go言語が初期開発段階から成熟していく過程で、言語設計者が並行処理のパターンを洗練させ、より安全で表現力豊かな構文を提供しようとした努力の一環と言えます。

## 前提知識の解説

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

1.  **チャネル (Channels)**:
    *   Goにおけるゴルーチン間の通信手段です。チャネルを通じて値を送受信することで、メモリ共有による競合状態を避けることができます。
    *   `make(chan Type, capacity)` で作成され、`capacity` が0の場合はバッファなし(同期)、0より大きい場合はバッファあり(非同期)チャネルとなります。
    *   `ch <- value` でチャネルに値を送信し、`value := <-ch` でチャネルから値を受信します。

2.  **`select` ステートメント**:
    *   複数のチャネル操作(送受信)のうち、準備ができたものを待機し、実行するための構文です。
    *   `case` 句にはチャネルの送受信操作を記述し、`default` 句はどのチャネル操作も準備ができていない場合に即座に実行されます。
    *   `select` は、チャネルからの値の「型」に基づいて分岐するものではなく、チャネル操作が「準備完了」になったかどうかで分岐します。

3.  **インターフェース (Interfaces)**:
    *   メソッドの集合を定義する型です。Goのインターフェースは、動的なポリモーフィズムを実現します。
    *   インターフェース型の変数は、内部的に「型」と「値」のペアを保持します。
    *   インターフェース変数が具体的な値を保持していない場合、そのインターフェース変数は `nil` となります。このとき、内部の「型」と「値」の両方が `nil` です。

4.  **`type switch` ステートメント**:
    *   インターフェース型の変数が保持している具体的な型に基づいて処理を分岐させるための構文です。
    *   `switch v := i.(type)` の形式で記述し、`case` 句で具体的な型を指定します。

5.  **`for ... range` ループ**:
    *   スライス、配列、マップ、文字列などのコレクションを反復処理するための構文です。
    *   通常、`for index, value := range collection` の形式で、インデックスと値(またはキーと値)を取得します。

## 技術的詳細

このコミットは、Go言語の仕様書 `doc/go_spec.html` に以下の重要な変更を加えています。

1.  **チャネルの `close` および `closed` 関数に関する記述の追加**:
    *   `make(chan int, 100)` の説明の後に、`close(c)` 関数のセマンティクスが追加されました。
    *   `close(c)` はチャネル `c` がそれ以上の送信操作を受け付けないようにマークします。
    *   既に送信された値がすべて受信された後、受信操作はチャネルの型のゼロ値を返します。
    *   少なくとも1つのゼロ値が受信された後、`closed(c)` 関数(後に `v, ok := <-ch` の `ok` 戻り値に統合される概念)が `true` を返すと記述されています。これは、チャネルが閉じられ、かつバッファ内のすべての値が消費されたことを示します。
    *   `Predeclared functions` のリストに `close` と `closed` が追加されました。

2.  **`type switch` における `nil` ケースのサポート**:
    *   `TypeSwitchCase` の文法定義が `TypeSwitchCase = "case" ( type | "nil" ) | "default" .` に変更され、`nil` が `case` 句で明示的に指定できるようになりました。
    *   インターフェース値が `nil` の場合、明示的な `nil` ケースまたは `default` ケースのみが実行されるという説明が追加されました。
    *   `f()` が返すインターフェース値に対する `type switch` の例に `case nil:` が追加され、`f() returns nil` と出力するコードが示されました。
    *   同等の `if/else if` 構造の例にも `if v == nil` のチェックが追加され、`nil` の扱いが明確化されました。

3.  **チャネルに対する `range` ループのサポート**:
    *   `range` 句の右辺の式の型に「チャネル」が追加されました。
    *   チャネルの場合、識別子リストは1つの識別子(受信する値)を含む必要があると記述されました。
    *   イテレーションはチャネルが閉じられるまでチャネルから値を受信し、チャネルが閉じられる前に送信されたゼロ値は処理しないと明記されました。これは、`for i := range ch` のループが、チャネルが閉じられると自動的に終了し、`close` によって生成されるゼロ値はループの対象とならないことを意味します。
    *   `filter` 関数の例が `for i := range src` を使用するように変更され、チャネルからの値のイテレーションがより簡潔に記述できるようになりました。

4.  **誤った `select on type` 例の削除**:
    *   `select` ステートメントのセクションから、`chan interface{}` 型のチャネル `ca` に対して `case i = <-ca:` と `case f = <-ca:` のように、受信する値の型に基づいて `select` しようとする誤った例が削除されました。この例は、`select` がチャネル操作の準備完了を待つものであり、受信する値の型に基づいて分岐するものではないというGoのセマンティクスに反していました。

これらの変更は、Go言語の並行処理モデルと型システムの理解を深め、より安全でイディオム的なコード記述を促進するために不可欠なものでした。

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

このコミットは、`doc/go_spec.html` ファイル内の以下のセクションに影響を与えています。

1.  **チャネルの `make` 関数に関する説明の直後**:
    *   `close(c)` と `closed(c)` のセマンティクスを説明する新しい `<p>` タグが追加されました。

2.  **`TypeSwitchStat` の文法定義**:
    *   `TypeSwitchCase` の定義が `case type | default` から `case ( type | "nil" ) | default` に変更されました。

3.  **`type switch` の説明と例**:
    *   インターフェース値が `nil` の場合の `type switch` の挙動を説明する新しい `<p>` タグが追加されました。
    *   `switch i := f().(type)` の例に `case nil:` 句が追加されました。
    *   同等の `if/else if` 構造の例に `if v == nil` 句が追加されました。

4.  **`RangeClause` の文法定義と説明**:
    *   `range` の右辺の式がチャネルも含むことを示す記述が追加されました。
    *   チャネルに対する `range` ループのセマンティクスを説明する新しい `<p>` タグが追加されました。

5.  **`select` ステートメントの例**:
    *   `var ca chan interface {}; ... select { ... }` という誤った `select on type` の例が完全に削除されました。

6.  **`Predeclared functions` リスト**:
    *   `<ul>` リストに `<li>close</li>` と `<li>closed</li>` が追加されました。

7.  **`filter` 関数の例**:
    *   `func filter(in chan <- int, out <-chan int, prime int)` の関数シグネチャが `func filter(src chan <- int, dst <-chan int, prime int)` に変更され、引数名がより明確になりました。
    *   `for { i := <-in; ... }` のループが `for i := range src { ... }` に変更されました。
    *   チャネルの引数名も `in` から `src`、`out` から `dst` に変更されました。

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

このコミットにおける「コード」とは、Go言語の仕様書であるHTMLドキュメント内の記述とGoのコード例を指します。

1.  **チャネルの `close` と `closed`**:
    *   追加された説明は、Goの並行処理におけるチャネルの終了処理の重要性を強調しています。`close` はチャネルの送信側がこれ以上値を送らないことを通知するメカニズムです。受信側は、チャネルが閉じられた後もバッファに残っている値を受け取ることができ、バッファが空になるとチャネルの型のゼロ値を受け取ります。このゼロ値を受け取った後、`closed(c)`(または `v, ok := <-ch` の `ok` が `false`)はチャネルが完全に閉じられたことを示します。これは、リソースのリークを防ぎ、ゴルーチンの正常な終了を調整するために不可欠な機能です。

2.  **`type switch` における `nil` ケース**:
    *   `type switch` で `case nil:` を直接記述できるようになったことは、インターフェースの `nil` 値の扱いをより直感的かつ簡潔にします。以前は `if i == nil` で `nil` をチェックし、それから `type switch` を使う必要がありましたが、この変更により、すべての型分岐ロジックを単一の `type switch` ステートメント内に統合できるようになりました。これは、特にエラーハンドリングや、異なる型のデータを処理する際に、コードの可読性と保守性を向上させます。

3.  **チャネルに対する `range` ループ**:
    *   `for i := range ch` の導入は、Goの並行処理における最も重要なイディオムの一つとなりました。この構文は、チャネルから値が送信されるたびにループを反復し、チャネルが `close` されると自動的にループを終了します。これにより、明示的な `for { select { ... } }` や `for { v, ok := <-ch; if !ok { break } ... }` といった冗長なコードを記述する必要がなくなります。`filter` 関数の例が示すように、これはチャネルからのストリーム処理を非常に簡潔かつ安全に記述するための強力なツールです。

4.  **誤った `select on type` 例の削除**:
    *   この削除は、Go言語の設計思想における「シンプルさ」と「明確さ」を反映しています。`select` はチャネル操作の準備完了を待つためのものであり、型に基づくディスパッチを行うものではありません。誤解を招く例を削除することで、Goの並行処理モデルの核心にあるチャネルと `select` の正しいセマンティクスがより明確になります。これは、Goプログラマーが言語の機能を正しく理解し、誤ったパターンを避ける上で非常に重要です。

これらの変更は、Go言語がその初期段階で並行処理の強力なプリミティブを提供しつつも、その利用パターンとセマンティクスを洗練させていく過程を示しています。

## 関連リンク

*   Go言語の公式ドキュメント: [https://go.dev/doc/](https://go.dev/doc/)
*   Go言語の仕様書 (現在のバージョン): [https://go.dev/ref/spec](https://go.dev/ref/spec)
*   Go Concurrency Patterns (Google I/O 2012): [https://go.dev/blog/concurrency-patterns](https://go.dev/blog/concurrency-patterns) (チャネルと `range` のイディオムについて深く解説されています)

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

*   Go言語のコミット履歴 (GitHub): [https://github.com/golang/go/commits/master](https://github.com/golang/go/commits/master)
*   Go言語の公式仕様書 (コミット時点のバージョンに近いもの、または現在の仕様書): [https://go.dev/ref/spec](https://go.dev/ref/spec)
*   Go言語のチャネルに関する一般的な解説記事やチュートリアル (例: Go by Example: Channels, Tour of Go: Channels)
*   Go言語の `select` ステートメントに関する解説記事
*   Go言語のインターフェースと `type switch` に関する解説記事
*   Go言語の `for ... range` ループに関する解説記事
*   Rob Pike の Go言語に関する講演や記事 (Go言語の設計思想を理解する上で役立ちます)