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

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

このコミットは、Go言語の公式仕様書である doc/go_spec.html ファイルに対する変更です。具体的には、range キーワードの動作に関する記述を修正し、より正確な表現に更新しています。

コミット

  • コミットハッシュ: bb3a32ef6e6772ca6fefda119d0238aec6f7e585
  • 作者: Robert Griesemer gri@golang.org
  • 日付: Mon May 20 13:27:53 2013 -0700

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

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

元コミット内容

spec: fix language about "range" clause

Fixes #5434.

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/9595044

変更の背景

このコミットの背景には、Go言語の range キーワードの動作に関する仕様記述の曖昧さがありました。特に、配列、ポインタ、またはスライスに対して range ループを使用し、かつ最初のイテレーション変数(通常はインデックス)のみを受け取る場合の挙動について、既存の記述が誤解を招く可能性がありました。

Go言語の range ループは、コレクション(配列、スライス、文字列、マップ、チャネル)を反復処理するための強力な構文です。通常、for ... range ... の形式で記述され、各イテレーションでキーと値(またはインデックスと値)のペアを返します。しかし、イテレーション変数を1つだけ指定した場合の動作は、コレクションの種類によって異なります。

このコミットでは、配列、ポインタ、スライスに対して range を使用し、かつインデックスのみを受け取る場合に、イテレーション値が len(a) ではなく len(a)-1 まで生成されることを明確にする必要がありました。これは、Go言語におけるインデックスが0から始まること、および len(a) が要素の「数」を示すため、最後の有効なインデックスは len(a)-1 であるという基本的な原則に合致させるための修正です。

この修正は、Go言語の仕様の正確性を保ち、開発者が range ループの挙動を正しく理解できるようにするために重要でした。

前提知識の解説

Go言語の仕様書 (The Go Programming Language Specification)

Go言語の仕様書は、Go言語の構文、セマンティクス、および標準ライブラリの動作を定義する公式文書です。Go言語の設計者によって維持されており、言語のあらゆる側面に関する究極の権威となります。開発者は、言語の特定の挙動について疑問が生じた場合、この仕様書を参照します。このコミットで変更された doc/go_spec.html は、この仕様書のHTML版の一部です。

range キーワード

Go言語の range キーワードは、for ループと組み合わせて使用され、配列、スライス、文字列、マップ、チャネルなどのコレクションを反復処理するために使われます。

基本的な構文は以下の通りです。

for index, value := range collection {
    // index と value を使用した処理
}

または、インデックスのみ、あるいは値のみが必要な場合は、アンダースコア _ を使用して不要な変数を破棄できます。

for index := range collection {
    // index を使用した処理
}

for _, value := range collection {
    // value を使用した処理
}

range の挙動はコレクションの種類によって異なります。

  • 配列、スライス、文字列: index は要素のインデックス、value はそのインデックスにある要素のコピーです。
  • マップ: index はキー、value はそのキーに対応する値です。マップの反復順序は保証されません。
  • チャネル: value はチャネルから受信した値です。チャネルが閉じられるまで値を受信し続けます。

このコミットで特に焦点を当てているのは、配列、ポインタ、スライスに対して for index := range collection の形式で range を使用した場合の挙動です。

len() 関数

len() はGo言語の組み込み関数で、配列、スライス、マップ、文字列、チャネルの長さを返します。

  • 配列、スライス、文字列の場合、要素の数を返します。
  • マップの場合、キーと値のペアの数を返します。
  • チャネルの場合、チャネル内のキューに入れられた(まだ受信されていない)要素の数を返します。

Go言語では、配列やスライスのインデックスは0から始まります。したがって、len(a) が配列 a の要素の総数である場合、有効なインデックスは 0 から len(a)-1 までとなります。

技術的詳細

このコミットの技術的な核心は、Go言語の仕様書における range ループの記述の微細ながらも重要な修正にあります。

変更前の仕様書では、配列、ポインタ、またはスライス a に対して range ループを使用し、かつ最初のイテレーション変数(インデックス)のみが指定された場合、イテレーション値が「0 から len(a) まで」生成されると記述されていました。

しかし、Go言語のインデックスは0から始まるため、len(a) は要素の総数を示し、最後の有効なインデックスは len(a)-1 です。もしイテレーション値が len(a) まで生成されると解釈されると、それは配列やスライスの範囲外アクセス(out-of-bounds access)を示唆することになり、Go言語の基本的なインデックス付けの原則と矛盾します。

このコミットでは、この記述を「0 から len(a)-1 まで」に修正することで、仕様の正確性を確保しています。これにより、for index := range a の形式でループを回した場合、index0, 1, ..., len(a)-1 の値を取ることが明確になります。この修正は、range ループが実際に配列やスライス自体をインデックス付けしない(つまり、a[index] のようなアクセスを行わない)という事実と整合しています。単にインデックス値のみを生成するということです。

この変更は、Go言語の range ループのセマンティクスをより厳密に定義し、開発者が誤解なくコードを記述できるようにするために不可欠でした。特に、range ループの内部実装や最適化を理解する上で、この正確な記述は重要です。

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

変更は 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 May 14, 2013",
+ 	"Subtitle": "Version of May 20, 2013",
  	"Path": "/ref/spec"
  }-->
  
@@ -4620,7 +4620,7 @@ channel         c  chan E, &lt;-chan E       element  e  E
 For an array, pointer to array, or slice value <code>a</code>, the index iteration
 values are produced in increasing order, starting at element index 0.
 If only the first iteration variable is present, the range loop produces
-iteration values from 0 up to <code>len(a)</code> and does not index into the array
+iteration values from 0 up to <code>len(a)-1</code> and does not index into the array
 or slice itself. For a <code>nil</code> slice, the number of iterations is 0.
 </li>
 

具体的には、以下の2箇所が変更されています。

  1. 仕様書のバージョン日付の更新: - "Subtitle": "Version of May 14, 2013", + "Subtitle": "Version of May 20, 2013", これは、仕様書が更新された日付を反映するための単純な変更です。

  2. range ループの記述の修正: -iteration values from 0 up to <code>len(a)</code> and does not index into the array +iteration values from 0 up to <code>len(a)-1</code> and does not index into the array これがこのコミットの主要な変更点であり、range ループがインデックスのみを生成する場合の挙動に関する記述を修正しています。

コアとなるコードの解説

このコミットの核心は、doc/go_spec.html 内の range ループに関する説明文の変更です。

変更前の記述: iteration values from 0 up to <code>len(a)</code> and does not index into the array (イテレーション値は0から len(a) まで生成され、配列をインデックス付けしない)

変更後の記述: iteration values from 0 up to <code>len(a)-1</code> and does not index into the array (イテレーション値は0から len(a)-1 まで生成され、配列をインデックス付けしない)

この修正は、Go言語の配列やスライスのインデックスが0から始まるという事実に基づいています。len(a) は配列やスライスの要素の総数を返しますが、有効なインデックスは 0 から len(a)-1 までです。したがって、for index := range a のようにインデックスのみを反復する場合、生成されるインデックス値は 0, 1, ..., len(a)-1 となります。

len(a) まで」という表現は、len(a) 自体も含まれるかのような誤解を招く可能性がありました。しかし、実際には len(a) は有効なインデックスではありません。この修正により、range ループがインデックスのみを生成する際の挙動が、Go言語のインデックス付けの慣習と完全に一致するように明確化されました。

また、「and does not index into the array or slice itself.」という部分は、for index := range a の形式では、range ループが実際に a[index] のような操作を行って要素にアクセスするわけではなく、単にインデックス値のみを生成していることを強調しています。これは、例えば nil スライスの場合でも、イテレーション回数が0になるという記述と整合しています。

この変更は、Go言語の仕様の正確性を高め、開発者が range ループの挙動をより正確に理解し、それに基づいて堅牢なコードを記述できるようにするために重要です。

関連リンク

参考にした情報源リンク