[インデックス 16956] ファイルの概要
このコミットは、以前に誤って提出された変更(CL 11884043)を元に戻すものです。具体的には、Go言語仕様のドキュメント doc/go_spec.html
におけるインデックス式とセレクタ式に関するいくつかの明確化を削除しています。これにより、Go言語仕様は、これらの変更が適用される前の状態に戻されました。
コミット
- コミットハッシュ:
9f75dd7f25d0171bb0b32522de41b252d6eef111
- 作者: Robert Griesemer gri@golang.org
- 日付: 2013年7月31日 14:10:46 -0700
- コミットメッセージ:
undo CL 11884043 / bfd5ed8236d5 CL submitted prematurely by mistake. ««« original CL description spec: clarify index and selector expressions 1) Explain a[i] and a[i:j] where a is of type *A as shortcut for (*a)[i] and (*a)[i:j], respectively. 2) Together with 1), because len() of nil slices is well defined, there's no need to special case nil operands anymore. 3) The result of indexing or slicing a constant string is always a non-constant byte or string value. 4) The result of slicing an untyped string is a value of type string. 5) If the operand of a valid slice a[i:j] is nil (i, j must be 0 for it to be valid - this already follows from the in-range rules), the result is a nil slice. Fixes #4913. Fixes #5951. R=rsc, r, iant, ken CC=golang-dev https://golang.org/cl/11884043 »»» R=r CC=golang-dev https://golang.org/cl/12170046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/9f75dd7f25d0171bb0b32522de41b252d6eef111
元コミット内容
このコミットは、以下の内容を含むCL 11884043を元に戻すものです。
「spec: インデックス式とセレクタ式を明確化
-
型が
*A
であるa
に対するa[i]
およびa[i:j]
を、それぞれ(*a)[i]
および(*a)[i:j]
のショートカットとして説明する。 -
1)と合わせて、
nil
スライスのlen()
が適切に定義されているため、nil
オペランドを特別扱いする必要がなくなる。 -
定数文字列のインデックス付けまたはスライス操作の結果は、常に非定数のバイトまたは文字列値である。
-
型なし文字列のスライス操作の結果は、
string
型の値である。 -
有効なスライス
a[i:j]
のオペランドがnil
の場合(有効であるためにはi, j
が0
でなければならない - これは既に範囲内ルールから導かれる)、結果はnil
スライスである。
Fixes #4913. Fixes #5951.
R=rsc, r, iant, ken CC=golang-dev https://golang.org/cl/11884043 」
変更の背景
このコミットの背景は、コミットメッセージに明記されている通り、「CL submitted prematurely by mistake.(CLが誤って時期尚早に提出された)」という点にあります。つまり、以前のCL 11884043は、Go言語仕様にインデックス式とセレクタ式に関する重要な明確化を導入しようとしましたが、何らかの理由でその変更がまだコミットされるべきではないと判断され、元に戻されたということです。
元のCLは、Go言語の仕様におけるいくつかの曖昧さや、開発者が直感的に理解しにくい挙動を解消することを目的としていました。特に、ポインタ型に対するインデックス操作の解釈、nil
スライスの扱い、定数文字列や型なし文字列のスライス結果の型など、Go言語の基本的なデータ構造と操作に関する詳細なルールを明確にしようとしていました。
しかし、これらの変更が時期尚早であった、あるいはさらなる議論や調整が必要であったため、この「undo」コミットによって一時的に取り消されました。これは、Go言語の仕様策定プロセスにおいて、変更が慎重に検討され、合意形成がなされるまで、安易に仕様に組み込まれないという厳格な姿勢を示しています。
前提知識の解説
このコミットの変更内容を理解するためには、Go言語の以下の概念について理解しておく必要があります。
- Go言語仕様 (Go Language Specification): Go言語の構文、セマンティクス、標準ライブラリの動作などを定義する公式ドキュメントです。Goプログラムの挙動は、この仕様に厳密に従って決定されます。
- インデックス式 (Index Expressions): 配列、スライス、文字列、マップなどの要素にアクセスするために使用される
a[x]
の形式の式です。- 配列 (Arrays): 固定長で同じ型の要素のシーケンスです。例:
var a [5]int
- スライス (Slices): 配列の一部を参照する動的なビューです。長さと容量を持ちます。
nil
スライスは、基になる配列を持たず、長さと容量が0のスライスです。 - 文字列 (Strings): 不変のバイトシーケンスです。GoではUTF-8でエンコードされたテキストを扱うことが一般的ですが、文字列はバイトのシーケンスとしてインデックス付けされます。
- マップ (Maps): キーと値のペアを格納するハッシュテーブルです。
- 配列 (Arrays): 固定長で同じ型の要素のシーケンスです。例:
- セレクタ式 (Selector Expressions): 構造体やインターフェースのフィールドやメソッドにアクセスするために使用される
x.f
の形式の式です。 - ポインタ (Pointers): 変数のメモリアドレスを保持する型です。Goでは、C/C++のようなポインタ演算は制限されていますが、変数のアドレスを渡すことで、関数が元の変数を変更できるようにします。
*A
は型A
へのポインタ型を示します。ポインタのデリファレンスは*p
のように行い、ポインタが指す値にアクセスします。 len()
とcap()
関数:len(s)
: スライス、配列、文字列、マップ、チャネルの長さを返します。スライスの場合、現在含まれている要素の数です。cap(s)
: スライス、配列、チャネルの容量を返します。スライスの場合、基になる配列の先頭からスライスが拡張できる最大要素数です。nil
スライスに対するlen()
とcap()
は、どちらも0
を返します。これはGoの仕様で明確に定義されています。
- 定数 (Constants): コンパイル時に値が決定される不変のデータです。数値、ブール値、文字列の定数があります。
- 型なし定数 (Untyped Constants): Goでは、リテラル(例:
10
,"hello"
,3.14
)はデフォルトで「型なし」の定数として扱われます。これらは、使用される文脈に応じて適切な型に変換されます。例えば、var i int = 10
の10
はint
型に変換されます。 - ランタイムパニック (Run-time Panics): Goプログラムの実行中に発生する回復不可能なエラーです。インデックスが範囲外である場合など、プログラムの継続が不可能と判断された場合に発生します。
技術的詳細
このコミットは、doc/go_spec.html
ファイルに対する変更を元に戻すことで、Go言語仕様の以下の点を以前の状態に戻しました。
-
ポインタ型に対するインデックス/スライス操作のショートカットの削除:
- 元のCLでは、型が
*A
(A
は配列型)であるa
に対するa[i]
やa[i:j]
を、それぞれ(*a)[i]
や(*a)[i:j]
のショートカットとして明示的に説明していました。このコミットにより、この明確化が削除されました。これは、Goのセレクタ式がポインタのデリファレンスを自動的に行うのと同様に、インデックス式もポインタのデリファレンスを自動的に行うという暗黙のルールに戻ったことを意味します。 - 同様に、構造体の匿名フィールドがポインタ型である場合のセレクタ式
x.f
が(*x.A).f
のショートカットであるという説明も元に戻されました。
- 元のCLでは、型が
-
nil
オペランドの特別扱いの削除:- 元のCLでは、
len()
がnil
スライスに対して適切に定義されているため、インデックス式やスライス式においてnil
オペランドを特別扱いする必要がないと説明していました。このコミットにより、この説明が削除され、配列やスライスに対するインデックス操作でnil
オペランドが与えられた場合にランタイムパニックが発生するという、より一般的なルールが強調される形に戻りました。 - 特に、スライスに対するインデックス操作において、「スライスが
nil
の場合、またはx
が実行時に範囲外の場合、ランタイムパニックが発生する」という記述が元に戻されました。
- 元のCLでは、
-
定数文字列のインデックス/スライス結果の型に関する明確化の削除:
- 元のCLでは、定数文字列のインデックス付けまたはスライス操作の結果が常に非定数のバイトまたは文字列値であると説明していました。このコミットにより、この明確化が削除されました。これにより、定数文字列から得られるバイトや文字列が、文脈によっては定数として扱われる可能性が残る、あるいはその挙動が仕様で明示的に規定されない状態に戻りました。
-
型なし文字列のスライス結果の型に関する明確化の削除:
- 元のCLでは、型なし文字列のスライス操作の結果が
string
型の値であると説明していました。このコミットにより、この明確化が削除されました。これにより、型なし文字列のスライス結果の型に関する挙動が、より一般的な型推論ルールに委ねられる形に戻りました。
- 元のCLでは、型なし文字列のスライス操作の結果が
-
有効な
nil
スライス操作の結果に関する明確化の削除:- 元のCLでは、「有効なスライス
a[i:j]
のオペランドがnil
の場合(有効であるためにはi, j
が0
でなければならない)、結果はnil
スライスである」という説明を追加していました。このコミットにより、この特定のルールが削除されました。
- 元のCLでは、「有効なスライス
これらの変更は、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 July 31, 2013",
+ "Subtitle": "Version of July 25, 2013",
"Path": "/ref/spec"
}-->
@@ -1909,7 +1909,7 @@ ShortVarDecl = IdentifierList ":=" ExpressionList .
</pre>
<p>
-It is shorthand for a regular <a href="#Variable_declarations">variable declaration</a>
+It is a shorthand for a regular <a href="#Variable_declarations">variable declaration</a>
with initializer expressions but no types:
</p>
@@ -2245,7 +2245,7 @@ element index plus one. A slice literal has the form
</pre>
<p>
-and is shorthand for a slice operation applied to an array:
+and is a shortcut for a slice operation applied to an array:
</p>
<pre>
@@ -2462,7 +2462,7 @@ is also a pointer to a struct, <code>x.y.z</code> is shorthand
for <code>(*(*x).y).z</code>, and so on.\n If <code>x</code> contains an anonymous field of type <code>*A</code>,\n where <code>A</code> is also a struct type,\n-<code>x.f</code> is shorthand for <code>(*x.A).f</code>.\n+<code>x.f</code> is a shortcut for <code>(*x.A).f</code>.\n </p>\n \n <p>\n@@ -2519,9 +2519,10 @@ a[x]\n </pre>\n \n <p>\n-denotes the element of the array, pointer to array, slice, string or map <code>a</code> indexed by <code>x</code>.\n-The value <code>x</code> is called the <i>index</i> or <i>map key</i>, respectively.\n-The following rules apply:\n+denotes the element of the array, slice, string or map <code>a</code> indexed by <code>x</code>.\n+The value <code>x</code> is called the\n+<i>index</i> or <i>map key</i>, respectively. The following\n+rules apply:\n </p>\n \n <p>\n@@ -2536,48 +2537,44 @@ If <code>a</code> is not a map:\n </ul>\n \n <p>\n-For <code>a</code> of <a href="#Array_types">array type</a> <code>A</code>:\n+For <code>a</code> of type <code>A</code> or <code>*A</code>\n+where <code>A</code> is an <a href="#Array_types">array type</a>:\n </p>\n <ul>\n \t<li>a <a href="#Constants">constant</a> index must be in range</li>\n-\t<li>if <code>x</code> is out of range at run time,\n+\t<li>if <code>a</code> is <code>nil</code> or if <code>x</code> is out of range at run time,\n \t a <a href="#Run_time_panics">run-time panic</a> occurs</li>\n \t<li><code>a[x]</code> is the array element at index <code>x</code> and the type of\n \t <code>a[x]</code> is the element type of <code>A</code></li>\n </ul>\n \n <p>\n-For <code>a</code> of <a href="#Pointer_types">pointer</a> to array type:\n-</p>\n-<ul>\n-\t<li><code>a[x]</code> is shorthand for <code>(*a)[x]</code></li>\n-</ul>\n-\n-<p>\n-For <code>a</code> of <a href="#Slice_types">slice type</a> <code>S</code>:\n+For <code>a</code> of type <code>S</code> where <code>S</code> is a <a href="#Slice_types">slice type</a>:\n </p>\n <ul>\n-\t<li>if <code>x</code> is out of range at run time,\n+\t<li>if the slice is <code>nil</code> or if <code>x</code> is out of range at run time,\n \t a <a href="#Run_time_panics">run-time panic</a> occurs</li>\n \t<li><code>a[x]</code> is the slice element at index <code>x</code> and the type of\n \t <code>a[x]</code> is the element type of <code>S</code></li>\n </ul>\n \n <p>\n-For <code>a</code> of <a href="#String_types">string type</a>:\n+For <code>a</code> of type <code>T</code>\n+where <code>T</code> is a <a href="#String_types">string type</a>:\n </p>\n <ul>\n \t<li>a <a href="#Constants">constant</a> index must be in range\n \t if the string <code>a</code> is also constant</li>\n \t<li>if <code>x</code> is out of range at run time,\n \t a <a href="#Run_time_panics">run-time panic</a> occurs</li>\n-\t<li><code>a[x]</code> is the non-constant byte value at index <code>x</code> and the type of\n+\t<li><code>a[x]</code> is the byte at index <code>x</code> and the type of\n \t <code>a[x]</code> is <code>byte</code></li>\n \t<li><code>a[x]</code> may not be assigned to</li>\n </ul>\n \n <p>\n-For <code>a</code> of <a href="#Map_types">map type</a> <code>M</code>:\n+For <code>a</code> of type <code>M</code>\n+where <code>M</code> is a <a href="#Map_types">map type</a>:\n </p>\n <ul>\n \t<li><code>x</code>\'s type must be\n@@ -2631,9 +2628,9 @@ a[low : high]\n </pre>\n \n <p>\n-constructs a substring or slice. The <i>indices</i> <code>low</code> and\n-<code>high</code> select which elements of operand <code>a</code> appear\n-in the result. The result has indices starting at 0 and length equal to\n+constructs a substring or slice. The indices <code>low</code> and\n+<code>high</code> select which elements appear in the result. The result has\n+indices starting at 0 and length equal to\n <code>high</code> - <code>low</code>.\n After slicing the array <code>a</code>\n </p>\n@@ -2666,34 +2663,24 @@ a[:] // same as a[0 : len(a)]\n </pre>\n \n <p>\n-If <code>a</code> is a pointer to an array, <code>a[low : high]</code> is shorthand for\n-<code>(*a)[low : high]</code>.\n-</p>\n-\n-<p>\n-For arrays or strings, the indices are <i>in range</i> if\n-<code>0</code> <= <code>low</code> <= <code>high</code> <= <code>len(a)</code>,\n+For arrays or strings, the indices <code>low</code> and <code>high</code> are\n+<i>in range</i> if <code>0</code> <= <code>low</code> <= <code>high</code> <= <code>len(a)</code>,\n otherwise they are <i>out of range</i>.\n For slices, the upper index bound is the slice capacity <code>cap(a)</code> rather than the length.\n A <a href="#Constants">constant</a> index must be non-negative and representable by a value of type\n <code>int</code>.\n-If both indices are constant, they must satisfy <code>low <= high</code>.\n-If the indices are out of range at run time, a <a href="#Run_time_panics">run-time panic</a> occurs.\n+If both indices\n+are constant, they must satisfy <code>low <= high</code>. If <code>a</code> is <code>nil</code>\n+or if the indices are out of range at run time, a <a href="#Run_time_panics">run-time panic</a> occurs.\n </p>\n \n <p>\n-Except for <a href="#Constants">untyped strings</a>, if the sliced operand is a string or slice,\n-the result of the slice operation is a non-constant value of the same type as the operand.\n-For untyped string operands the result is a non-constant value of type <code>string</code>.\n+If the sliced operand is a string or slice, the result of the slice operation\n+is a string or slice of the same type.\n If the sliced operand is an array, it must be <a href="#Address_operators">addressable</a>\n and the result of the slice operation is a slice with the same element type as the array.\n </p>\n \n-<!-- TODO: should this be an implementation restriction? -->\n-<p>\n-If the sliced operand of a valid slice expression is a <code>nil</code> slice, the result\n-is a <code>nil</code> slice.\n-<p>\n \n <h3 id=\"Type_assertions\">Type assertions</h3>
コアとなるコードの解説
上記の差分は、Go言語仕様のHTMLドキュメント doc/go_spec.html
から、インデックス式とスライス式に関するいくつかの説明が削除されたことを示しています。
具体的には、以下の変更が元に戻されました。
- ドキュメントのバージョン日付の変更: サブタイトルが「Version of July 31, 2013」から「Version of July 25, 2013」に戻されました。これは、このコミットが以前の変更を元に戻すものであることを明確に示しています。
- 「shorthand」から「a shorthand」への変更: 「shorthand」という表現が「a shorthand」に戻されました。これは文法的な微調整であり、意味的な大きな変更ではありません。
- インデックス式におけるポインタ型配列の扱い:
- 以前の変更では、「配列、ポインタ型配列、スライス、文字列、マップ
a
の要素をx
でインデックス付けする」という説明に「ポインタ型配列」が明示的に含まれていました。このコミットにより、この記述が削除され、「配列、スライス、文字列、マップa
の要素をx
でインデックス付けする」という元の表現に戻りました。 - さらに、「ポインタ型配列
a
の場合:a[x]
は(*a)[x]
のショートカットである」というセクションが完全に削除されました。これは、Goのインデックス式がポインタの自動デリファレンスを暗黙的に行うという挙動を、仕様で明示的に記述しない方針に戻ったことを意味します。
- 以前の変更では、「配列、ポインタ型配列、スライス、文字列、マップ
- スライスに対するインデックス操作での
nil
の扱い:- スライス型
S
のa
の場合の説明で、「x
が実行時に範囲外の場合、ランタイムパニックが発生する」という記述が、「スライスがnil
の場合、またはx
が実行時に範囲外の場合、ランタイムパニックが発生する」という記述に戻されました。これは、nil
スライスに対するインデックス操作がパニックを引き起こすという挙動を、より明確に記述する形に戻ったことを示します。
- スライス型
- 文字列に対するインデックス操作でのバイト値の型:
- 文字列型
T
のa
の場合の説明で、「a[x]
はインデックスx
の非定数バイト値であり、a[x]
の型はbyte
である」という記述が、「a[x]
はインデックスx
のバイトであり、a[x]
の型はbyte
である」という記述に戻されました。これにより、「非定数」という修飾が削除されました。
- 文字列型
- スライス式におけるポインタ型配列の扱い:
- 「
a
が配列へのポインタの場合、a[low : high]
は(*a)[low : high]
のショートカットである」というセクションが完全に削除されました。これもインデックス式と同様に、ポインタの自動デリファレンスを仕様で明示的に記述しない方針に戻ったことを意味します。
- 「
- スライス式における
nil
の扱いと結果の型:- スライス式の範囲チェックに関する説明で、「
a
がnil
の場合、またはインデックスが実行時に範囲外の場合、ランタイムパニックが発生する」という記述が追加されました。 - 「型なし文字列を除き、スライスされたオペランドが文字列またはスライスの場合、スライス操作の結果はオペランドと同じ型の非定数値である。型なし文字列オペランドの場合、結果は
string
型の非定数値である。」という説明が、「スライスされたオペランドが文字列またはスライスの場合、スライス操作の結果は同じ型の文字列またはスライスである。」という簡潔な記述に戻されました。これにより、結果が「非定数」であるという明確化が削除されました。 - 「有効なスライス式のスライスされたオペランドが
nil
スライスの場合、結果はnil
スライスである。」という段落が完全に削除されました。
- スライス式の範囲チェックに関する説明で、「
これらの変更は、Go言語の仕様が、特定のケースに対する詳細なルールを明示的に記述するのではなく、より一般的な原則に基づいて動作することを好む傾向があることを示唆しています。
関連リンク
- 元の変更 (CL 11884043): https://golang.org/cl/11884043
- このコミット (CL 12170046): https://golang.org/cl/12170046
- 関連するIssue:
- Issue 4913: https://golang.org/issue/4913
- Issue 5951: https://golang.org/issue/5951
参考にした情報源リンク
- The Go Programming Language Specification: https://go.dev/ref/spec
- Go Slices: usage and internals: https://go.dev/blog/slices
- Effective Go - Slices: https://go.dev/doc/effective_go#slices
- Go言語のポインタについて: https://go.dev/doc/effective_go#pointers (一般的なGoのポインタに関する情報源)
- Go言語の定数: https://go.dev/doc/effective_go#constants (一般的なGoの定数に関する情報源)