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

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

このコミットは、Go言語の公式仕様書である doc/go_spec.html ファイルに対する変更です。このファイルは、Go言語の構文、セマンティクス、組み込み関数、パッケージなど、言語のあらゆる側面を詳細に記述した、Goプログラマーにとっての最終的な参照ドキュメントです。

コミット

  • コミットハッシュ: cc3f21cefeb24c214487d89ebe50818c08e37d88
  • 作者: Robert Griesemer gri@golang.org
  • コミット日時: 2012年12月3日 月曜日 14:23:41 -0800
  • コミットメッセージ:
    spec: channel operations are restricted by the channel direction
    
    Also:
    - 'for' statements with a range clause do not accept send-only
       channels
    - '_, _ = range ch' is not equivalent to "_ = range ch" if ch
       is a channel (rewriting the latter to the former leads to
       an invalid range clause).
    
    These clarifications document the status quo.
    
    R=rsc, r, iant, ken
    CC=golang-dev
    https://golang.org/cl/6874053
    

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

https://github.com/golang/go/commit/cc3f21cefeb24c214487d89ebe50818c08e37d88

元コミット内容

spec: channel operations are restricted by the channel direction

Also:
- 'for' statements with a range clause do not accept send-only
   channels
- '_, _ = range ch' is not equivalent to "_ = range ch" if ch
   is a channel (rewriting the latter to the former leads to
   an invalid range clause).

These clarifications document the status quo.

R=rsc, r, iant, ken
CC=golang-dev
https://golang.org/cl/6874053

変更の背景

このコミットの主な目的は、Go言語のチャネル操作に関する仕様をより明確にすることです。特に、チャネルの「方向性」(送信用、受信用、双方向)が、そのチャネルに対して実行できる操作(送信、受信、for...rangeによるイテレーション)にどのように影響するかを明文化しています。

コミットメッセージにある「These clarifications document the status quo.(これらの明確化は現状を文書化したものである)」という記述は非常に重要です。これは、このコミットによってGo言語のチャネルの動作自体が変更されたわけではなく、既存の動作が仕様書に正確に反映されていなかった点を修正したことを意味します。

Goのような厳密に型付けされた言語において、仕様の曖昧さはプログラマーの混乱を招き、コンパイラの挙動の一貫性を損なう可能性があります。特にチャネルはGoの並行処理の根幹をなす要素であり、その挙動は極めて明確である必要があります。このコミットは、チャネルの方向性による操作の制限、特にfor...range文における制約について、仕様書に明記することで、言語の正確性と堅牢性を高めることを目的としています。

前提知識の解説

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

  1. Goのチャネル (Channels): Go言語におけるチャネルは、ゴルーチン間で値を安全に送受信するための通信メカニズムです。チャネルは型付けされており、特定の型の値のみを送受信できます。チャネルは、make(chan Type)のように作成され、chan Typeは双方向チャネルを意味します。

  2. チャネルの方向性 (Channel Direction): Goのチャネルは、その使用方法に応じて方向性を指定できます。

    • 双方向チャネル (chan T): 送信も受信も可能です。
    • 送信用チャネル (chan<- T): 送信のみが可能です。このチャネルからは値を受信できません。
    • 受信用チャネル (<-chan T): 受信のみが可能です。このチャネルに値を送信できません。 チャネルの方向性は、関数の引数などでチャネルの利用方法を制限するために使用されます。これにより、コードの意図が明確になり、誤用を防ぐことができます。
  3. 送信操作 (ch <- value): チャネルに値を送信する操作です。この操作は、チャネルがバッファリングされていない場合や、バッファが満杯の場合、受信側が値を受け取るまでブロックします。

  4. 受信操作 (<-ch): チャネルから値を受信する操作です。この操作は、チャネルに値が送信されるまでブロックします。

  5. for...range文とチャネル: Go言語のfor...range文は、配列、スライス、文字列、マップ、そしてチャネルをイテレートするために使用できます。チャネルに対してfor...rangeを使用すると、チャネルがクローズされるまで、チャネルから値が受信されるたびにループが実行されます。チャネルがクローズされると、ループは終了します。

  6. ブランク識別子 (_): Go言語のブランク識別子(アンダースコア)は、変数を宣言したがその値を使用しない場合に、コンパイラのエラーを回避するために使用されます。例えば、for...rangeループでインデックスや値の片方だけが必要な場合などに使われます。

技術的詳細

このコミットは、Go言語仕様の以下のセクションに具体的な変更を加えています。

  1. 受信操作の制限の明確化: 変更前は、受信操作について「チャネルchから受信された値」とだけ記述されていました。変更後は、「チャネルの方向性は受信操作を許可しなければならない」という文言が追加されました。これは、chan<- T(送信用チャネル)からは値を受信できないという、Goの基本的なチャネルの方向性ルールを明文化したものです。

  2. 送信操作の制限の明確化: 同様に、送信操作についても「チャネルの方向性は送信操作を許可しなければならない」という文言が追加されました。これは、<-chan T(受信用チャネル)には値を送信できないというルールを明文化したものです。

  3. for...range文におけるチャネルの制限: これがこのコミットの最も重要な変更点の一つです。

    • 変更前は、for...rangeのレンジ式として「チャネル」が許可されるとだけ書かれていました。
    • 変更後は、「受信操作を許可するチャネル」という具体的な条件が追加されました。これにより、chan<- T(送信用チャネル)はfor...range文で使用できないことが明確になりました。for...rangeはチャネルからの値の受信に依存するため、受信できないチャネルでは機能しません。
    • また、for...rangeのテーブル記述において、チャネルの型としてchan Eだけでなく、<-chan E(受信用チャネル)も明示的にリストアップされました。これは、受信用チャネルがfor...rangeで有効であることを強調しています。
  4. for...rangeのイテレーション変数とブランク識別子のニュアンス: コミットメッセージで言及されている「_, _ = range ch_ = range ch と等価ではない」という点は、Goのfor...rangeの挙動における重要な詳細です。

    • Goのfor...rangeは、レンジ式の種類によってイテレーション変数の数が異なります。例えば、マップや配列/スライスでは2つの変数を取ることができます(キー/インデックスと値)。
    • しかし、チャネルの場合、for...rangeは常に1つのイテレーション変数(受信した値)しか持ちません
    • したがって、for _, value := range ch のように2つの変数を指定しようとすると、コンパイルエラーになります。これは、チャネルのfor...rangeが2つの値を生成しないためです。
    • 仕様書の変更は、この点について直接的な構文の変更を加えていませんが、既存の記述「2番目のイテレーション変数がブランク識別子である場合、レンジ句は最初の変数のみが存在する場合と同じである」という文言の文脈を「後者の場合(つまり、2つのイテレーション変数が許可される場合)」に限定することで、チャネルのように1つの変数しか許可されないケースには適用されないことを暗に示しています。これは、既存のコンパイラの挙動を仕様に合わせるための明確化です。

これらの変更は、Go言語のチャネルのセマンティクスをより厳密に定義し、プログラマーがチャネルを扱う際の誤解を減らすことを目的としています。

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

変更は 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 November 29, 2012",
+	"Subtitle": "Version of December 3, 2012",
 	"Path": "/ref/spec"
 }-->
 
@@ -3249,8 +3249,9 @@
 <p>
 For an operand <code>ch</code> of <a href="#Channel_types">channel type</a>,
 the value of the receive operation <code>&lt;-ch</code> is the value received
-from the channel <code>ch</code>. The type of the value is the element type of
-the channel. The expression blocks until a value is available.\n+from the channel <code>ch</code>. The channel direction must permit receive operations,
+and the type of the receive operation is the element type of the channel.
+The expression blocks until a value is available.
 Receiving from a <code>nil</code> channel blocks forever.
 Receiving from a <a href="#Close">closed</a> channel always succeeds,
 immediately returning the element type\'s <a href="#The_zero_value">zero
@@ -3873,8 +3874,9 @@
 
 <p>
 A send statement sends a value on a channel.
-The channel expression must be of <a href="#Channel_types">channel type</a>
-and the type of the value must be <a href="#Assignability">assignable</a>
+The channel expression must be of <a href="#Channel_types">channel type</a>,
+the channel direction must permit send operations,
+and the type of the value to be sent must be <a href="#Assignability">assignable</a>
 to the channel\'s element type.
 </p>
 
@@ -4319,12 +4321,13 @@
 
 <p>
 The expression on the right in the "range" clause is called the <i>range expression</i>,
-which may be an array, pointer to an array, slice, string, map, or channel.
+which may be an array, pointer to an array, slice, string, map, or channel permitting
+<a href="#Receive_operator">receive operations</a>.
 As with an assignment, the operands on the left must be
 <a href="#Address_operators">addressable</a> or map index expressions; they
 denote the iteration variables. If the range expression is a channel, only
-one iteration variable is permitted, otherwise there may be one or two.
-If the second iteration variable is the <a href="#Blank_identifier">blank identifier</a>,\n+one iteration variable is permitted, otherwise there may be one or two. In the latter case,
+if the second iteration variable is the <a href="#Blank_identifier">blank identifier</a>,
 the range clause is equivalent to the same clause with only the first variable present.
 </p>
 
@@ -4342,7 +4345,7 @@
 array or slice  a  [n]E, *[n]E, or []E    index    i  int    a[i]       E
 string          s  string type            index    i  int    see below  rune
 map             m  map[K]V                key      k  K      m[k]       V
-channel         c  chan E                 element  e  E
+channel         c  chan E, <-chan E       element  e  E
 </pre>
 
 <ol>

コアとなるコードの解説

上記の差分は、Go言語仕様の以下の箇所を修正しています。

  1. 仕様書のバージョン日付の更新: Subtitleが「Version of November 29, 2012」から「Version of December 3, 2012」に更新されました。これは、仕様書の内容が更新されたことを示す標準的な変更です。

  2. 受信操作のセクション (#Receive_operator):

    • 変更前: 「受信操作 <-ch の値は、チャネル ch から受信された値です。値の型はチャネルの要素型です。」
    • 変更後: 「受信操作 <-ch の値は、チャネル ch から受信された値です。チャネルの方向性は受信操作を許可しなければならず、 受信操作の型はチャネルの要素型です。」
    • 解説: 受信操作を行うチャネルは、受信が許可されている(つまり、送信用チャネルではない)必要があることを明示しています。これは、chan<- T 型のチャネルから値を受信しようとするとコンパイルエラーになるという既存の言語挙動を仕様に反映したものです。
  3. 送信ステートメントのセクション (#Send_statements):

    • 変更前: 「チャネル式はチャネル型でなければならず、値の型はチャネルの要素型に代入可能でなければなりません。」
    • 変更後: 「チャネル式はチャネル型でなければならず、チャネルの方向性は送信操作を許可しなければならず、 送信する値の型はチャネルの要素型に代入可能でなければなりません。」
    • 解説: 送信操作を行うチャネルは、送信が許可されている(つまり、受信用チャネルではない)必要があることを明示しています。これは、<-chan T 型のチャネルに値を送信しようとするとコンパイルエラーになるという既存の言語挙動を仕様に反映したものです。
  4. for...range句のセクション (#For_statements):

    • 変更前: 「range句の右側の式は、レンジ式と呼ばれ、配列、配列へのポインタ、スライス、文字列、マップ、またはチャネルである場合があります。」

    • 変更後: 「range句の右側の式は、レンジ式と呼ばれ、配列、配列へのポインタ、スライス、文字列、マップ、または受信操作を許可するチャネルである場合があります。」

    • 解説: for...range文でチャネルをイテレートする場合、そのチャネルは受信操作を許可している必要があることを明確にしています。これにより、chan<- T(送信用チャネル)をfor...rangeで使用しようとするとコンパイルエラーになることが仕様で裏付けられました。

    • 変更前: 「もし2番目のイテレーション変数がブランク識別子である場合、レンジ句は最初の変数のみが存在する場合と同じです。」

    • 変更後: 「後者の場合、もし2番目のイテレーション変数がブランク識別子である場合、レンジ句は最初の変数のみが存在する場合と同じです。」

    • 解説: この変更は、文脈をより明確にするためのものです。「後者の場合」とは、2つのイテレーション変数が許可されるレンジ式(例: マップ)の場合を指します。チャネルのfor...rangeは常に1つのイテレーション変数しか持たないため、このルールはチャネルには適用されないことを暗に示しています。

  5. for...rangeのレンジ式とイテレーション変数の表:

    • channel c chan E element e E
    • 変更後: channel c chan E, <-chan E element e E
    • 解説: for...rangeでイテレート可能なチャネルの型として、双方向チャネル (chan E) に加えて、受信用チャネル (<-chan E) も明示的にリストアップされました。これは、受信用チャネルもfor...rangeで有効であることを強調し、上記の「受信操作を許可するチャネル」という条件と整合しています。

これらの変更は、Go言語のチャネルの挙動、特に方向性による制約とfor...range文での使用について、仕様書をより正確かつ詳細に記述するためのものです。

関連リンク

参考にした情報源リンク