[インデックス 10737] ファイルの概要
このコミットは、Go言語の仕様書(doc/go_spec.html
)における比較演算子のセクションを更新し、構造体(struct)と配列(array)の比較可能性に関するルールを明確化・拡張するものです。また、インターフェースの比較がパニックを引き起こす条件や、nil
との比較が特別な構文であることを明確にしています。
コミット
commit 83f648c9625343045da1e6b4ecc3d207c84403b3
Author: Russ Cox <rsc@golang.org>
Date: Mon Dec 12 22:21:46 2011 -0500
spec: allow comparison of structs, arrays containing comparable values
Also, clarify when interface comparison panics and
that comparison to nil is a special syntax rather than
a general comparison rule.
R=r, gri, r, iant, cw, bradfitz
CC=golang-dev
https://golang.org/cl/5440117
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/83f648c9625343045da1e6b4ecc3d207c84403b3
元コミット内容
このコミットの元の内容は、Go言語の仕様書を更新し、構造体と配列が比較可能な値を含む場合に比較可能であることを許可すること、およびインターフェースの比較がパニックを引き起こす条件とnil
との比較が一般的な比較ルールではなく特別な構文であることを明確にすることです。
変更の背景
Go言語の初期のバージョンでは、構造体や配列の比較には制限がありました。特に、==
や!=
演算子は、配列や構造体には適用できないとされていました。しかし、プログラミングの利便性や直感性を考慮すると、これらの複合型がその要素やフィールドが比較可能であれば、全体としても比較可能であるべきというニーズがありました。
このコミットは、Go言語の設計と進化の一環として、型の比較可能性に関するルールをより柔軟かつ強力にするために導入されました。これにより、開発者は構造体や配列を直接比較できるようになり、コードの記述が簡素化され、より自然な表現が可能になります。
また、インターフェースの比較におけるパニックの発生条件や、nil
との比較の特殊性についても、仕様書上での明確な記述が求められていました。これは、Goの型システムにおけるインターフェースの振る舞いや、nil
の概念が他の言語とは異なる側面を持つため、誤解を避けるための重要な改善です。
前提知識の解説
このコミットの変更内容を理解するためには、以下のGo言語の基本的な概念を理解しておく必要があります。
- 比較演算子 (
==
,!=
,<
,<=
,>
,>=
): Go言語における値の比較を行うための演算子。 - 型システム: Go言語がどのようにデータ型を定義し、それらがどのように相互作用するかを規定するシステム。
- 比較可能性 (Comparability): ある型の値が
==
や!=
演算子によって比較できるかどうかを示す特性。Go言語では、すべての型が比較可能であるわけではありません。例えば、スライス、マップ、関数は通常比較できません。 - 順序付け可能性 (Orderability): ある型の値が
<
,<=
,>
,>=
などの順序演算子によって比較できるかどうかを示す特性。 - 構造体 (Struct): 異なる型のフィールドをまとめた複合データ型。
- 配列 (Array): 同じ型の要素を固定長で並べた複合データ型。
- インターフェース (Interface): メソッドのシグネチャの集合を定義する型。インターフェース型の変数は、そのインターフェースが定義するすべてのメソッドを実装する任意の型の値を保持できます。
nil
: Go言語におけるゼロ値の一つで、ポインタ、スライス、マップ、関数、インターフェース、チャネルなどの参照型が何も指していない状態を表します。
技術的詳細
このコミットは、Go言語の仕様書における比較演算子の定義を大幅に改訂しています。主な変更点は以下の通りです。
-
構造体と配列の比較可能性の導入:
- 以前の仕様では、構造体と配列は
==
および!=
演算子の対象外とされていました。 - 新しい仕様では、「すべてのフィールドが比較可能であれば、構造体は比較可能である」と明記されました。2つの構造体は、対応するすべてのフィールドが等しい場合に等しいとされます。
- 同様に、「配列の要素型が比較可能であれば、配列は比較可能である」と明記されました。2つの配列は、対応するすべての要素が等しい場合に等しいとされます。
- これにより、例えば
struct { X int; Y string }
のような構造体や、[3]int
のような配列は、その内部の型が比較可能であるため、直接==
や!=
で比較できるようになりました。
- 以前の仕様では、構造体と配列は
-
インターフェース比較の明確化とパニック条件:
- インターフェース値は比較可能であると定義されました。
- 2つのインターフェース値は、動的な型が同一であり、動的な値が等しい場合、または両方が
nil
である場合に等しいとされます。 - 重要な追加として、「同一の動的な型を持つ2つのインターフェース値の比較は、その型の値が比較可能でない場合、実行時パニックを引き起こす」というルールが明記されました。これは、直接的なインターフェース値の比較だけでなく、インターフェース値の配列やインターフェース値のフィールドを持つ構造体の比較にも適用されます。例えば、
[]interface{}
の中にスライスやマップ(これらは比較不可能)が含まれている場合、そのインターフェース値同士を比較しようとするとパニックが発生します。
-
nil
比較の特殊性の強調:- スライス、マップ、関数は比較不可能であると再確認されました。
- しかし、特別なケースとして、これらの値は
nil
と比較できることが明確にされました。これは、nil
がこれらの型のゼロ値として機能するためです。 - ポインタ、チャネル、インターフェース値の
nil
との比較も許可されており、これは一般的な比較ルールから導かれると説明されています。
これらの変更は、Go言語の型システムにおける比較のセマンティクスをより厳密かつ包括的に定義し、開発者が期待する振る舞いと実際の言語の振る舞いの間のギャップを埋めることを目的としています。特に、構造体と配列の比較可能性の導入は、Go言語の表現力を高める上で重要な一歩でした。
コアとなるコードの変更箇所
このコミットは、Go言語の仕様書であるdoc/go_spec.html
ファイルを変更しています。具体的な変更箇所は、比較演算子に関するセクションです。
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -2909,72 +2909,103 @@ Comparison operators compare two operands and yield a value of type <code>bool</\n != not equal\n < less\n <= less or equal\n-> greater\n->= greater or equal\n+> greater\n+>= greater or equal\n </pre>\n \n <p>\n-The operands must be <i>comparable</i>; that is, the first operand\n+In any comparison, the first operand\n must be <a href=\"#Assignability\">assignable</a>\n to the type of the second operand, or vice versa.\n </p>\n <p>\n-The operators <code>==</code> and <code>!=</code> apply\n-to operands of all types except arrays and structs.\n-All other comparison operators apply only to integer, floating-point\n-and string values. The result of a comparison is defined as follows:\n+The equality operators <code>==</code> and <code>!=</code> apply\n+to operands that are <i>comparable</i>.\n+The ordering operators <code><</code>, <code><=</code>, <code>></code>, and <code>>=</code>\n+apply to operands that are <i>ordered</i>.\n+These terms and the result of the comparisons are defined as follows:\n </p>\n \n <ul>\n <li>\n-\tInteger values are compared in the usual way.\n+\tBoolean values are comparable.\n+\tTwo boolean values are equal if they are either both\n+\t<code>true</code> or both <code>false</code>.\n </li>\n+\n <li>\n-\tFloating point values are compared as defined by the IEEE-754\n-\tstandard.\n+\tInteger values are comparable and ordered, in the usual way.\n </li>\n+\t\n <li>\n-\tTwo complex values <code>u</code>, <code>v</code> are\n-\tequal if both <code>real(u) == real(v)</code> and\n-\t<code>imag(u) == imag(v)</code>.\n+\tFloating point values are comparable and ordered,\n+\tas defined by the IEEE-754 standard.\n </li>\n+\t\n <li>\n-\tString values are compared byte-wise (lexically).\n+\tComplex values are comparable.\n+\tTwo complex values <code>u</code> and <code>v</code> are\n+\tequal if both <code>real(u) == real(v)</code> and\n+\t<code>imag(u) == imag(v)</code>.\n </li>\n+\t\n <li>\n-\tBoolean values are equal if they are either both\n-\t<code>true</code> or both <code>false</code>.\n+\tString values are comparable and ordered, lexically byte-wise.\n </li>\n+\t\n <li>\n-\tPointer values are equal if they point to the same location\n-\tor if both are <code>nil</code>.\n+\tPointer values are comparable.\n+\tTwo pointer values are equal if they point to the same location or if both have value <code>nil</code>.\n </li>\n+\t\n <li>\n-\tA slice, map, or function value may be compared only to <code>nil</code>.\n+\tChannel values are comparable.\n+\tTwo channel values are equal if they were created by the same call to <code>make</code>\n+\t(§<a href=\"#Making_slices_maps_and_channels\">Making slices, maps, and channels</a>)\n+\tor if both have value <code>nil</code>.\n </li>\n+\n <li>\n-\tChannel values are equal if they were created by the same call to <code>make</code>\n-\t(§<a href=\"#Making_slices_maps_and_channels\">Making slices, maps, and channels</a>)\n-\tor if both are <code>nil</code>.\n+\tInterface values are comparable.\n+\tTwo interface values are equal if they have <a href=\"#Type_identity\">identical</a> dynamic types\n+\tand equal dynamic values or if both have value <code>nil</code>.\n </li>\n+\t\n <li>\n-\tInterface values are equal if they have <a href=\"#Type_identity\">identical</a> dynamic types and\n-\tequal dynamic values or if both are <code>nil</code>.\n+\tA value <code>x</code> of non-interface type <code>X</code> and\n+\ta value <code>t</code> of interface type <code>T</code> are comparable when values\n+\tof type <code>X</code> are comparable and\n+\t<code>X</code> implements <code>T</code>.\n+\tThey are equal if <code>t</code>\'s dynamic type is identical to <code>X</code>\n+\tand <code>t</code>\'s dynamic value is equal to <code>x</code>.\n </li>\n+\n <li>\n-\tAn interface value <code>x</code> is equal to a non-interface value\n-\t<code>y</code> if the dynamic type of <code>x</code> is identical to\n-\tthe static type of <code>y</code> and the dynamic value of <code>x</code>\n-\tis equal to <code>y</code>.\n+\tStruct values are comparable if all the fields are comparable.\n+\tTwo struct values are equal if their corresponding fields are equal.\n </li>\n+\t\n <li>\n-\tA pointer, function, slice, channel, map, or interface value is equal\n-\tto <code>nil</code> if it has been assigned the explicit value\n-\t<code>nil</code>, if it is uninitialized, or if it has been assigned\n-\tanother value equal to <code>nil</code>.\n+\tArray values are comparable if values of the array element type are comparable.\n+\tTwo array values are equal if their corresponding elements are equal.\n </li>\n </ul>\n \n+<p>\n+A comparison of two interface values with identical dynamic types\n+causes a <a href=\"#Run_time_panics\">run-time panic</a> if values\n+of that type are not comparable. This behavior applies not only to direct interface\n+value comparisons but also when comparing arrays of interface values\n+or structs with interface-valued fields.\n+</p>\n+\n+<p>\n+Slice, map, and function values are not comparable.\n+However, as a special case, a slice, map, or function value may\n+be compared to the predeclared identifier <code>nil</code>.\n+Comparison of pointer, channel, and interface values to <code>nil</code>\n+is also allowed and follows from the general rules above.\n+</p>\n```
## コアとなるコードの解説
変更された`doc/go_spec.html`のセクションは、Go言語の比較演算子の振る舞いを定義するものです。
* **旧仕様の削除**:
* `The operators == and != apply to operands of all types except arrays and structs.` (配列と構造体を除くすべての型のオペランドに`==`と`!=`が適用される) という記述が削除されました。これは、構造体と配列の比較を許可するための前提となる変更です。
* `All other comparison operators apply only to integer, floating-point and string values.` (他のすべての比較演算子は整数、浮動小数点、文字列の値にのみ適用される) という記述も削除され、より詳細な比較ルールに置き換えられました。
* **新仕様の追加と変更**:
* 比較演算子`==`と`!=`が「比較可能な」オペランドに適用され、順序演算子`<`, `<=`, `>`, `>=`が「順序付け可能な」オペランドに適用されるという、より一般的な定義が導入されました。
* 各組み込み型(ブーリアン、整数、浮動小数点、複素数、文字列、ポインタ、チャネル、インターフェース)の比較可能性と順序付け可能性が個別に定義されました。
* **構造体と配列の比較可能性に関する新しいルールが追加されました。**
* `Struct values are comparable if all the fields are comparable. Two struct values are equal if their corresponding fields are equal.` (すべてのフィールドが比較可能であれば、構造体は比較可能である。2つの構造体は、対応するフィールドがすべて等しい場合に等しい。)
* `Array values are comparable if values of the array element type are comparable. Two array values are equal if their corresponding elements are equal.` (配列の要素型が比較可能であれば、配列は比較可能である。2つの配列は、対応する要素がすべて等しい場合に等しい。)
* インターフェースの比較に関する詳細な説明が追加され、特に「**同一の動的な型を持つ2つのインターフェース値の比較は、その型の値が比較可能でない場合、実行時パニックを引き起こす**」という重要な警告が追加されました。
* スライス、マップ、関数が比較不可能であること、しかし`nil`との比較は特別なケースとして許可されることが明確に記述されました。
これらの変更により、Go言語の比較セマンティクスはより厳密になり、特に複合型の比較に関する振る舞いが明確化され、開発者にとってより予測可能で強力な機能が提供されるようになりました。
## 関連リンク
* Go言語の仕様書: [https://go.dev/ref/spec](https://go.dev/ref/spec) (現在の最新版)
* Go言語のブログ: [https://go.dev/blog/](https://go.dev/blog/) (Go言語の進化に関する情報源)
## 参考にした情報源リンク
* Go言語の公式ドキュメント
* Go言語のGitHubリポジトリのコミット履歴
* Go言語の比較演算子に関する一般的な情報源 (IEEE 754など)