[インデックス 14641] ファイルの概要
このコミットは、Go言語のランタイムにおけるマップ操作の挙動変更に関するものです。具体的には、nil
マップに対して要素の削除(delete
関数)を行った際の挙動を、パニック(panic)から何もしない(no-op)に変更しています。これにより、nil
マップに対する削除操作がより安全かつ予測可能になります。
コミット
commit 28a50c7f51ea031f91b47421322be981a5a0d8a6
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Thu Dec 13 23:48:48 2012 +0800
runtime: deletion on nil maps is a no-op now
Fixes #4535.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6942044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/28a50c7f51ea031f91b47421322be981a5a0d8a6
元コミット内容
runtime: deletion on nil maps is a no-op now
このコミットは、nil
マップに対する要素の削除操作が、パニックを引き起こす代わりに何もしない(no-op)ように変更することを目的としています。これは、Go言語のIssue #4535を修正するものです。
変更の背景
Go言語において、マップ(map
)は参照型であり、初期化されていないマップ変数はnil
値を取ります。Go 1.0の時点では、nil
マップからの要素の読み取りや、nil
マップへの要素の追加はパニックを引き起こしました。しかし、nil
マップからの要素の削除(delete
組み込み関数を使用)もまたパニックを引き起こす挙動でした。
この挙動は、特にマップがオプションである場合や、関数がnil
マップを受け取る可能性がある場合に、開発者にとって不便であり、予期せぬクラッシュの原因となる可能性がありました。例えば、マップが空であるかどうかにかかわらず、特定のキーを確実に削除したい場合、nil
チェックを毎回行う必要がありました。
このコミットは、nil
マップに対するdelete
操作がパニックを引き起こすという既存の挙動を、何もしない(no-op)というより寛容な挙動に変更することで、この問題を解決しようとしています。これにより、開発者はdelete(m, key)
を呼び出す前にm != nil
のチェックを行う必要がなくなり、コードの記述が簡潔になります。この変更は、Go言語の設計哲学である「実用性」と「簡潔さ」に沿ったものです。
前提知識の解説
Go言語のマップ (map)
Go言語のマップは、キーと値のペアを格納するための組み込みのデータ構造です。他の言語のハッシュマップ、ハッシュテーブル、辞書に相当します。
- 宣言と初期化:
var m map[string]int // 宣言のみ。m は nil マップ m = make(map[string]int) // make を使って初期化
nil
マップ:var m map[KeyType]ValueType
のように宣言されたマップは、初期値としてnil
を持ちます。nil
マップは、キーと値のペアを保持するためのメモリが割り当てられていない状態です。nil
マップからの読み取り: ゼロ値を返します(パニックは起こしません)。nil
マップへの書き込み: パニックを引き起こします。nil
マップからの削除(このコミット以前): パニックを引き起こしました。nil
マップの長さ(len(m)
): 0を返します(パニックは起こしません)。
delete
組み込み関数:delete(m, key)
は、マップm
から指定されたkey
とその値のペアを削除するために使用されます。キーが存在しない場合でも、エラーは発生せず、何もしません。
Goランタイム
Goプログラムは、Goランタイムと呼ばれる軽量な実行環境上で動作します。ランタイムは、ガベージコレクション、スケジューリング、マップやスライスなどの組み込み型の実装など、多くの低レベルな機能を提供します。このコミットで変更されているsrc/pkg/runtime/hashmap.c
は、Goのマップの内部実装の一部であり、C言語で書かれています。
技術的詳細
このコミットの技術的詳細の中心は、Goランタイム内のマップ削除処理を司るruntime·mapdelete
関数の変更です。
変更前は、runtime·mapdelete
関数内で、引数として渡されたマップのハッシュテーブルポインタh
がnil
である場合に、runtime·panicstring("deletion of entry in nil map")
を呼び出してパニックを発生させていました。
変更後は、このパニックを発生させるコードが削除され、代わりにreturn;
が追加されています。これにより、h
がnil
(つまり、nil
マップ)の場合、関数は即座に処理を終了し、何もせずに呼び出し元に戻ります。結果として、nil
マップに対する削除操作はパニックを引き起こさず、何もしない(no-op)挙動となります。
この変更は、Go言語のユーザーがdelete
関数をより柔軟に利用できるようにするためのものです。例えば、以下のようなコードがパニックを起こさなくなります。
var m map[string]int // m は nil
delete(m, "some_key") // この行はパニックを起こさなくなる
テストファイルtest/fixedbugs/issue4535.go
が追加されており、この変更が意図通りに機能することを確認しています。このテストは、nil
マップを宣言し、そのマップに対してdelete
操作を実行するだけのシンプルなものです。このテストがパニックを起こさずに正常に実行されれば、変更が成功したことを意味します。
コアとなるコードの変更箇所
src/pkg/runtime/hashmap.c
ファイルの runtime·mapdelete
関数内の変更です。
--- a/src/pkg/runtime/hashmap.c
+++ b/src/pkg/runtime/hashmap.c
@@ -989,7 +989,7 @@ runtime·mapdelete(MapType *t, Hmap *h, ...)
byte *ak;
if(h == nil)
- runtime·panicstring("deletion of entry in nil map");
+ return;
if(raceenabled)
runtime·racewritepc(h, runtime·getcallerpc(&t), runtime·mapdelete);
また、この変更を検証するための新しいテストファイルが追加されています。
--- /dev/null
+++ b/test/fixedbugs/issue4535.go
@@ -0,0 +1,12 @@
+// run
+
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func main() {
+ var m map[int]int
+ delete(m, 0)
+}
コアとなるコードの解説
src/pkg/runtime/hashmap.c
の変更
runtime·mapdelete
関数は、Go言語のdelete
組み込み関数がマップから要素を削除する際に内部的に呼び出されるランタイム関数です。
if(h == nil)
: この条件は、削除操作の対象となるマップのハッシュテーブルポインタh
がnil
であるかどうかをチェックしています。Goのマップ変数がnil
である場合、その内部的なハッシュテーブルポインタもnil
になります。- 変更前:
runtime·panicstring("deletion of entry in nil map");
h
がnil
の場合、この行が実行され、「deletion of entry in nil map」というメッセージと共にパニックが発生していました。 - 変更後:
return;
h
がnil
の場合、パニックを発生させる代わりに、関数は単にreturn
し、処理を終了します。これにより、nil
マップに対する削除操作はエラーにならず、何もしない挙動となります。
test/fixedbugs/issue4535.go
の追加
このテストファイルは、変更された挙動を検証するために作成されました。
package main
: 実行可能なプログラムであることを示します。func main() { ... }
: プログラムのエントリポイントです。var m map[int]int
:int
型のキーとint
型の値を持つマップm
を宣言しています。この時点ではm
は初期化されていないため、nil
マップです。delete(m, 0)
:nil
マップm
からキー0
を削除しようとしています。このコミットの変更により、この行はパニックを起こさずに正常に実行されるはずです。
このテストは、// run
というコメントが付いていることから、Goのテストフレームワークによって実行され、パニックが発生しないことを確認するものです。もしパニックが発生すれば、テストは失敗します。
関連リンク
- Go CL 6942044: https://golang.org/cl/6942044
参考にした情報源リンク
- Go言語の公式ドキュメント(マップに関するセクション)
- Go言語のソースコード(
src/pkg/runtime/hashmap.c
) - Go言語のIssueトラッカー(Issue #4535に関する議論、もし見つかれば)
- (注: 今回のWeb検索では直接的なIssue #4535のページは見つかりませんでしたが、コミットメッセージに記載されているため、過去に存在した問題であると推測されます。)
- Go言語のリリースノート(該当するバージョンでのマップの挙動変更に関する記述)