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

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

このコミットは、Go 1.3のリリースノートである doc/go1.3.html ファイルに、小さなマップのイテレーション順序に関する重要な変更点を追記するものです。具体的には、Go 1.1で導入された新しいマップ実装において、8つ以下のエントリを持つマップのイテレーション順序がランダム化されていなかった問題と、Go 1.3でそのランダム化が再導入されたことについて説明しています。これにより、開発者が意図しないイテレーション順序に依存したコードを記述するのを防ぎ、既存のバグを顕在化させることを目的としています。

コミット

doc/go1.3.html: add note about small map iteration order

LGTM=r
R=r
CC=golang-codereviews
https://golang.org/cl/98290048

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

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

元コミット内容

doc/go1.3.html: add note about small map iteration order

LGTM=r
R=r
CC=golang-codereviews
https://golang.org/cl/98290048

変更の背景

Go言語の仕様では、マップ(map)のイテレーション順序は保証されておらず、実行ごとに異なる可能性があります。これは、開発者がマップの内部実装に依存したコードを書くことを防ぎ、将来のGoのバージョンアップでマップの実装が変更されても、既存のコードが壊れないようにするための重要な設計原則です。

しかし、Go 1.1で導入された新しいマップ実装において、8つ以下のエントリを持つ「小さなマップ」のイテレーション順序が、意図せず一貫した順序で処理されるというバグがありました。この一貫性はシステムによって異なる可能性がありましたが、結果として一部の開発者はこの「一貫性」に依存したコードを書いてしまいました。特にテストコードにおいて、この問題が顕著に現れることがありました。

Go 1.3では、この問題に対処するため、小さなマップに対してもイテレーション順序のランダム化を再導入しました。このコミットは、このランダム化の再導入について、Go 1.3のリリースノートである doc/go1.3.html に明記することで、開発者にこの変更を周知し、意図しない順序依存のバグを修正するよう促すことを目的としています。

前提知識の解説

Go言語のマップ (map) とイテレーション順序

Go言語の map は、キーと値のペアを格納するための組み込みデータ型です。他の多くのプログラミング言語におけるハッシュマップ、ハッシュテーブル、連想配列に相当します。Goのマップの重要な特性の一つは、そのイテレーション(要素の走査)順序が保証されないという点です。

Goの公式仕様には、「マップのイテレーション順序は指定されておらず、あるイテレーションから次のイテレーションまで同じであることは保証されない」と明記されています。これは、マップの内部実装がハッシュテーブルであり、要素の格納順序がハッシュ値やメモリ配置に依存するためです。Goランタイムは、開発者がこの未指定の順序に依存しないように、意図的にイテレーションの開始位置をランダム化するなどの工夫を凝らしています。これにより、特定の順序に依存するバグが開発段階で顕在化しやすくなります。

Go 1.1でのマップ実装の変更

Go 1.1では、マップのパフォーマンスとメモリ効率を向上させるために、その内部実装が大幅に改善されました。この新しい実装は、特に小さなマップにおいて、イテレーション順序のランダム化のメカニズムが不完全でした。具体的には、8つ以下のエントリを持つマップの場合、イテレーションの開始位置がランダム化されず、結果として比較的安定した(しかし保証されない)順序で要素が返されることがありました。この挙動は、Goの仕様に反するものであり、開発者が誤ってこの「安定した順序」に依存するコードを書いてしまう原因となりました。

go vet ツール

go vet は、Goプログラムの潜在的なバグや疑わしい構造を検出するための静的解析ツールです。このツールは、コンパイルエラーにはならないが、実行時に問題を引き起こす可能性のあるコードパターンを特定するのに役立ちます。このコミットの文脈では、go vetunsafe.Pointer の不正な変換など、特定の危険なコードパターンを識別できることが言及されており、これはGoの型安全性を維持するための重要なツールであることを示しています。

技術的詳細

このコミット自体はGoランタイムのコード変更ではなく、Go 1.3のリリースノート (doc/go1.3.html) へのドキュメント追加です。しかし、このドキュメントが説明している技術的変更は、Goランタイムのマップ実装における重要な修正を指しています。

Go 1.0では、マップのイテレーション順序への依存を防ぐために、各マップイテレーションをマップ内のランダムなインデックスから開始するようにしていました。これは、マップの内部構造を走査する際に、常に同じ開始点からではなく、毎回異なる開始点から走査することで、順序依存のコードが早期に発見されるようにするための工夫です。

Go 1.1で導入された新しいマップ実装は、このランダム化のメカニズムを、8つ以下のエントリを持つ小さなマップに対して適用し忘れていました。その結果、これらの小さなマップでは、イテレーション順序がシステム間で異なる可能性はあったものの、特定のシステム上では一貫した順序で要素が返されることがありました。この「一貫性」は保証されたものではなく、Goの仕様に反するものでしたが、Go 1.1およびGo 1.2のプログラムがこの挙動に依存してしまい、特定のシステムでしか正しく動作しないバグを生み出す原因となりました。

Go 1.3では、この問題を修正し、小さなマップに対してもイテレーションのランダム化を再導入しました。これにより、意図しない順序依存のバグが顕在化し、開発者がそれらを修正せざるを得ない状況を作り出します。ドキュメントでは、特にテストコードでこの問題が頻繁に発生する可能性があると指摘しており、テストが特定のイテレーション順序に依存している場合に、Go 1.3へのアップデート後にテストが失敗する可能性があることを警告しています。

この変更は、Goの「マップのイテレーション順序は保証されない」という基本原則を再強制し、より堅牢で移植性の高いGoプログラムの記述を促進することを目的としています。

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

このコミットは、Goランタイムのコード自体を変更するものではなく、Go 1.3のリリースノートである doc/go1.3.html ファイルに以下の内容を追加しています。

--- a/doc/go1.3.html
+++ b/doc/go1.3.html
@@ -137,6 +137,28 @@ to <code>unsafe.Pointer</code> is illegal and must be rewritten.\n Such code can be identified by <code>go vet</code>.\n </p>\n \n+<h3 id="map">Map iteration</h3>\n+\n+<p>\n+Iterations over small maps no longer happen in a consistent order.\n+Go 1 defines that &ldquo;<a href="http://golang.org/ref/spec#For_statements">The iteration order over maps\n+is not specified and is not guaranteed to be the same from one iteration to the next.</a>&rdquo;\n+To keep code from depending on map iteration order,\n+Go 1.0 started each map iteration at a random index in the map.\n+A new map implementation introduced in Go 1.1 neglected to randomize\n+iteration for maps with eight or fewer entries, although the iteration order\n+can still vary from system to system.\n+This has allowed people to write Go 1.1 and Go 1.2 programs that\n+depend on small map iteration order and therefore only work reliably on certain systems.\n+Go 1.3 reintroduces random iteration for small maps in order to flush out these bugs.\n+</p>\n+\n+<p>\n+<em>Updating</em>: If code assumes a fixed iteration order for small maps,\n+it will break and must be rewritten not to make that assumption.\n+Because only small maps are affected, the problem arises most often in tests.\n+</p>\n+\n <h3 id="liblink">The linker</h3>

追加された内容は、Map iteration という新しいセクションとして、既存の unsafe.Pointer に関する記述の後に挿入されています。

コアとなるコードの解説

追加されたHTMLスニペットは、Go 1.3におけるマップのイテレーション順序に関する変更を説明するものです。

  1. <h3>Map iteration</h3>: 新しいセクションのタイトルです。
  2. 最初の <p> タグ:
    • 「小さなマップのイテレーションは、もはや一貫した順序では行われません。」と明確に述べています。
    • Go 1の仕様で「マップのイテレーション順序は指定されておらず、あるイテレーションから次のイテレーションまで同じであることは保証されない」と定義されていることを強調し、その仕様へのリンクを提供しています。
    • Go 1.0では、コードがマップのイテレーション順序に依存しないように、各マップイテレーションをランダムなインデックスから開始していたことを説明しています。
    • Go 1.1で導入された新しいマップ実装が、8つ以下のエントリを持つマップに対してイテレーションのランダム化を怠っていたことを指摘しています。これにより、イテレーション順序はシステムによって異なる可能性があったものの、特定のシステムでは一貫した順序で動作してしまい、Go 1.1およびGo 1.2のプログラムがこの挙動に依存してしまったと説明しています。
    • 「Go 1.3は、これらのバグを洗い出すために、小さなマップに対するランダムなイテレーションを再導入します。」と、Go 1.3での修正の目的を明確に述べています。
  3. 2番目の <p> タグ (<em>Updating</em> セクション):
    • Go 1.3へのアップデートに関する注意点として、「もしコードが小さなマップの固定されたイテレーション順序を仮定している場合、それは壊れ、その仮定をしないように書き直す必要があります。」と警告しています。
    • 影響を受けるのは小さなマップのみであるため、この問題は「ほとんどの場合、テストで発生する」と具体的に指摘しており、開発者がテストコードを見直す必要があることを示唆しています。

このドキュメントの追加は、Goの設計原則である「イテレーション順序の非保証」を再確認させ、開発者がその原則に従った堅牢なコードを書くことを促すための重要な情報提供です。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (特にGo 1.3リリースノートの該当セクション)
  • コミットメッセージと差分情報 (git diff)