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

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

このコミットは、Go言語の標準ライブラリ reflect パッケージ内の DeepEqual 関数のドキュメントを改善することを目的としています。特に、マップの比較における挙動(キーは == で比較され、値はディープイコールで比較される点)を明確にし、古い表現を修正しています。

コミット

reflect: improve documentation for DeepEqual regarding maps Keys use ==; values use deep equality. Also remove the word 'member'. Fixes #4258.

R=golang-dev, rsc CC=golang-dev https://golang.org/cl/6812055

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

https://github.com/golang/go/commit/8884fabfd739c232c97158b75a6ad0fe72cf9721

元コミット内容

commit 8884fabfd739c232c97158b75a6ad0fe72cf9721
Author: Rob Pike <r@golang.org>
Date:   Tue Oct 30 14:42:47 2012 -0700

    reflect: improve documentation for DeepEqual regarding maps
    Keys use ==; values use deep equality. Also remove the word 'member'.
    Fixes #4258.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/6812055
---
 src/pkg/reflect/deepequal.go | 8 +++++---\n 1 file changed, 5 insertions(+), 3 deletions(-)\n
diff --git a/src/pkg/reflect/deepequal.go b/src/pkg/reflect/deepequal.go
index cd364dd9fd..db047963eb 100644
--- a/src/pkg/reflect/deepequal.go
+++ b/src/pkg/reflect/deepequal.go
@@ -122,9 +122,11 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool
 	panic("Not reached")
 }
 
-// DeepEqual tests for deep equality. It uses normal == equality where possible
-// but will scan members of arrays, slices, maps, and fields of structs. It correctly
-// handles recursive types. Functions are equal only if they are both nil.\n+// DeepEqual tests for deep equality. It uses normal == equality where\n+// possible but will scan elements of arrays, slices, maps, and fields of\n+// structs. In maps, keys are compared with == but elements use deep\n+// equality. DeepEqual correctly handles recursive types. Functions are equal\n+// only if they are both nil.\n // An empty slice is not equal to a nil slice.\n func DeepEqual(a1, a2 interface{}) bool {\n 	if a1 == nil || a2 == nil {\n

変更の背景

このコミットの主な背景は、Go言語の reflect パッケージにある DeepEqual 関数のドキュメントの曖昧さを解消することです。特に、マップの比較に関する挙動が不明瞭であったため、それを明確にする必要がありました。コミットメッセージにある Fixes #4258 から、この変更がGoのIssue 4258に対応するものであることがわかります。

Issue 4258は、reflect.DeepEqual がマップを比較する際に、キーが == 演算子で比較され、値がディープイコールで比較されるという重要な詳細がドキュメントに明記されていないという問題提起でした。また、ドキュメント内で使用されていた「member」という単語が、配列やスライス、構造体のフィールドに対しては適切であるものの、マップの要素を指す場合には「element」の方がより適切であるという指摘もありました。

このコミットは、これらの点を修正し、DeepEqual の動作をより正確かつ理解しやすくすることを目的としています。

前提知識の解説

Go言語の reflect パッケージ

reflect パッケージは、Goプログラムが実行時に自身の構造を検査(リフレクション)することを可能にする機能を提供します。これにより、型情報、フィールド、メソッドなどを動的に操作できます。DeepEqual 関数は、このパッケージの一部であり、2つの値が「深く」等しいかどうかを再帰的に比較するために使用されます。

== 演算子とディープイコール

Go言語における == 演算子は、プリミティブ型(数値、文字列、ブール値など)や、ポインタ、チャネル、関数、インターフェース、構造体(フィールドがすべて比較可能である場合)、配列(要素がすべて比較可能である場合)の比較に使用されます。しかし、スライス、マップ、関数(nil以外)は == 演算子では直接比較できません。

  • == 演算子: 2つの値がメモリ上で同じであるか、または値がビット単位で同じであるかを比較します。構造体や配列の場合、すべてのフィールド/要素が == で比較可能であれば、それらのフィールド/要素がすべて等しい場合に true を返します。
  • ディープイコール (Deep Equality): reflect.DeepEqual が提供する比較方法です。これは、2つの値が同じ型であり、かつその内容が再帰的に等しいかどうかを判断します。例えば、2つのスライスが同じ要素を同じ順序で持っている場合、それらはディープイコールであると見なされます。マップの場合、キーと値のペアがすべて等しい場合にディープイコールと見なされます。

Go言語におけるマップの挙動

Go言語のマップは、キーと値のペアを格納するハッシュテーブルです。マップのキーは比較可能でなければなりません。これは、キーが == 演算子で比較できる型である必要があることを意味します。例えば、スライスや関数はマップのキーにはなれません。

マップの比較において、reflect.DeepEqual は以下のルールに従います。

  1. 両方のマップが nil であるか、両方が nil でないが長さが同じであるかをチェックします。
  2. 各マップのキーを反復処理し、一方のマップに存在するキーがもう一方のマップにも存在するかどうかを確認します。キーの比較には == 演算子が使用されます。
  3. 対応するキーが見つかった場合、そのキーに関連付けられた値が DeepEqual を使用して再帰的に比較されます。

このコミット以前のドキュメントでは、この「キーは ==、値はディープイコール」という重要なニュアンスが明確に記述されていませんでした。

技術的詳細

このコミットは、src/pkg/reflect/deepequal.go ファイル内の DeepEqual 関数のコメントブロックを修正しています。

変更前は、DeepEqual の説明が以下のようになっていました。

// DeepEqual tests for deep equality. It uses normal == equality where possible
// but will scan members of arrays, slices, maps, and fields of structs. It correctly
// handles recursive types. Functions are equal only if they are both nil.

この説明には以下の問題点がありました。

  1. マップの比較の詳細が不明確: マップのキーと値がどのように比較されるかについて言及がありませんでした。
  2. 「member」という単語の不適切さ: 配列、スライス、構造体に対して「member」という単語は適切ですが、マップの要素を指す場合には「element」の方がより一般的で正確です。

コミットによって、このコメントは以下のように変更されました。

// DeepEqual tests for deep equality. It uses normal == equality where
// possible but will scan elements of arrays, slices, maps, and fields of
// structs. In maps, keys are compared with == but elements use deep
// equality. DeepEqual correctly handles recursive types. Functions are equal
// only if they are both nil.

この変更により、以下の点が改善されました。

  1. マップの比較ルールの明確化: In maps, keys are compared with == but elements use deep equality. という一文が追加され、マップのキーが == で比較され、値がディープイコールで比較されるという重要な挙動が明示されました。これにより、開発者は DeepEqual がマップに対してどのように動作するかを正確に理解できるようになりました。
  2. 「member」から「elements」への変更: scan membersscan elements に変更され、より一般的な用語が使用されるようになりました。これは、マップの文脈においてもより自然な表現です。

このドキュメントの改善は、DeepEqual の正確な動作を理解する上で非常に重要であり、特にマップを扱う際の混乱を避けるのに役立ちます。

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

--- a/src/pkg/reflect/deepequal.go
+++ b/src/pkg/reflect/deepequal.go
@@ -122,9 +122,11 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool
 	panic("Not reached")
 }
 
-// DeepEqual tests for deep equality. It uses normal == equality where possible
-// but will scan members of arrays, slices, maps, and fields of structs. It correctly
-// handles recursive types. Functions are equal only if they are both nil.\n+// DeepEqual tests for deep equality. It uses normal == equality where\n+// possible but will scan elements of arrays, slices, maps, and fields of\n+// structs. In maps, keys are compared with == but elements use deep\n+// equality. DeepEqual correctly handles recursive types. Functions are equal\n+// only if they are both nil.\n // An empty slice is not equal to a nil slice.\n func DeepEqual(a1, a2 interface{}) bool {\n 	if a1 == nil || a2 == nil {\n

コアとなるコードの解説

このコミットは、reflect/deepequal.go ファイル内の DeepEqual 関数のドキュメンテーションコメントのみを変更しています。実際の関数のロジックには一切変更がありません。

変更されたコメントは、DeepEqual 関数の動作を説明するものです。

  • - で始まる行は削除された元のコメントです。
  • + で始まる行は追加された新しいコメントです。

主な変更点は以下の通りです。

  1. scan members から scan elements への変更:

    • 元のコメント: but will scan members of arrays, slices, maps, and fields of structs.
    • 新しいコメント: but will scan elements of arrays, slices, maps, and fields of structs.
    • これは、「member」という単語がマップの文脈では不適切であるという指摘に対応したものです。より一般的な「elements」を使用することで、配列、スライス、マップの要素、および構造体のフィールドを包括的に指すようになりました。
  2. マップの比較に関する詳細の追加:

    • 新しいコメントに In maps, keys are compared with == but elements use deep equality. という文が追加されました。
    • この文は、DeepEqual がマップを比較する際の具体的な挙動を明確にしています。すなわち、マップのキーはGoの通常の == 演算子で比較され、それに対応する値は DeepEqual 関数自体によって再帰的に(ディープイコールで)比較されるという点です。これは、DeepEqual の重要な側面であり、以前のドキュメントでは欠落していました。

この変更は、コードの動作自体には影響を与えませんが、開発者が DeepEqual を正しく理解し、予期せぬ挙動に遭遇するのを防ぐ上で非常に価値のあるドキュメントの改善です。

関連リンク

参考にした情報源リンク

  • Go言語公式ドキュメント reflect パッケージ: https://pkg.go.dev/reflect
  • Go言語におけるマップの比較に関する情報 (一般的なGoのドキュメントやチュートリアル)
  • Go言語における == 演算子と等価性に関する情報 (一般的なGoのドキュメントやチュートリアル)