[インデックス 10004] ファイルの概要
コミット
コミットハッシュ: bdf66114c7ad02b41b83522d7e9073cf0957d836
作成者: Russ Cox rsc@golang.org
作成日: 2011年10月17日 14:51:54 (UTC-4)
メッセージ: http: do not depend on map iteration order
影響範囲: src/pkg/http/client_test.go
(4行追加、2行削除)
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/bdf66114c7ad02b41b83522d7e9073cf0957d836
元コミット内容
commit bdf66114c7ad02b41b83522d7e9073cf0957d836
Author: Russ Cox <rsc@golang.org>
Date: Mon Oct 17 14:51:54 2011 -0400
http: do not depend on map iteration order
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5284050
src/pkg/http/client_test.go | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
変更の背景
このコミットは2011年10月17日に行われており、Go 1.0がリリースされる前(2012年3月)の重要な時期にあたります。当時、Go開発チームは1.0リリースに向けて言語仕様とランタイムの安定化を進めており、その過程でマップの反復順序の非決定性が問題となっていました。
2011年の時点で、Go開発チームは開発者がマップの反復順序に依存することを防ぐために、意図的にマップの反復順序をランダム化する方針を決定していました。この変更は、HTTPクライアントのテストコードがマップの反復順序に依存していることを発見し、それを修正するために行われました。
前提知識の解説
Go言語におけるマップの反復順序について
Go言語では、マップ(map)の反復順序は意図的に非決定的に設計されています。これは以下の理由によります:
- 移植性の確保: 実装間での動作の違いを避けるため
- セキュリティの向上: ハッシュ衝突攻撃を防ぐため
- 実装詳細への依存防止: 開発者が偶発的にマップの反復順序に依存することを防ぐため
言語仕様における規定
Go言語仕様では、マップの反復順序について以下のように明記されています:
"The iteration order over maps is not specified and is not guaranteed to be the same from one iteration to the next."
ランダム化の実装
Go 1.0以降、ランタイムはマップの反復順序をランダム化するために以下の仕組みを採用しています:
- ハッシュ関数のシード値をランダム化
- 開始位置をランダム化
- バケット内オフセットをランダム化
技術的詳細
マップの反復順序ランダム化の歴史
- 初期実装(Go 1.0以前): マップの反復順序は実装依存で、一定の規則性があった
- Go 1.0での変更: 反復順序の完全なランダム化を導入
- Go 1.1での改善: 小さなマップ(8要素以下)でもランダム化を強化
- Go 1.3での完全化: 全てのマップサイズでランダム化を保証
HTTPクライアントテストでの問題
HTTPクライアントのテストコードにおいて、以下のような問題が発生していたと推測されます:
- テストの決定性: マップの反復順序に依存したテストケースが存在
- 期待値の不一致: 順序が変わることで期待する結果と異なる結果が発生
- テストの不安定性: テスト実行のたびに結果が変わる可能性
コアとなるコードの変更箇所
src/pkg/http/client_test.go
において、6行の変更が行われました:
- 4行追加
- 2行削除
具体的な変更内容は以下の方針で行われたと考えられます:
- マップの反復順序に依存しない実装への変更
- テスト結果の比較方法の改善
- 順序を保証したい場合のソート処理の追加
コアとなるコードの解説
一般的なマップの反復順序依存問題の解決パターン
// 問題のあるコード例(反復順序に依存)
func processHeaders(headers map[string]string) string {
var result string
for key, value := range headers {
result += key + ":" + value + "\n"
}
return result
}
// 修正されたコード例(順序を保証)
func processHeaders(headers map[string]string) string {
var keys []string
for key := range headers {
keys = append(keys, key)
}
sort.Strings(keys)
var result string
for _, key := range keys {
result += key + ":" + headers[key] + "\n"
}
return result
}
テストコードでの典型的な修正パターン
// 修正前:順序に依存するテスト
func TestHTTPHeaders(t *testing.T) {
headers := map[string]string{
"Content-Type": "application/json",
"Authorization": "Bearer token",
}
result := processHeaders(headers)
expected := "Content-Type:application/json\nAuthorization:Bearer token\n"
if result != expected {
t.Errorf("Expected %q, got %q", expected, result)
}
}
// 修正後:順序に依存しないテスト
func TestHTTPHeaders(t *testing.T) {
headers := map[string]string{
"Content-Type": "application/json",
"Authorization": "Bearer token",
}
result := processHeaders(headers)
// 各ヘッダーが含まれているかを個別に確認
if !strings.Contains(result, "Content-Type:application/json") {
t.Error("Content-Type header not found")
}
if !strings.Contains(result, "Authorization:Bearer token") {
t.Error("Authorization header not found")
}
}
関連リンク
- Go言語仕様書: https://go.dev/ref/spec#For_statements
- Go Blog - Go maps in action: https://go.dev/blog/maps
- Go Issue #6719: https://github.com/golang/go/issues/6719 (小さなマップの反復順序ランダム化)
- Go Issue #54500: https://github.com/golang/go/issues/54500 (マップの反復順序修正提案)
参考にした情報源リンク
- Go's map iteration order is random | Hacker News: https://news.ycombinator.com/item?id=7655948
- Stack Overflow - Go map iteration order: https://stackoverflow.com/questions/9619479/go-what-determines-the-iteration-order-for-map-keys
- Go Design History: https://golang.design/history/
- Go maps iteration documentation: https://bitfieldconsulting.com/posts/map-iteration
- Go HTTP client testing: https://go.dev/src/net/http/client_test.go
このコミットは、Go言語の設計思想である「明示的で予測可能な動作」を実現するための重要な一歩であり、現在のGoの堅牢性の基盤となっています。