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

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

このコミットは、Go言語のテストスイートの一部である test/typeswitch.go ファイルに対する変更です。このファイルは、Go言語における型スイッチ(type switch)の機能が正しく動作するかを検証するためのテストコードを含んでいます。型スイッチは、インターフェース型の変数が実行時に保持している具体的な型に基づいて処理を分岐させるためのGo言語の強力な機能です。

コミット

commit 08eeb2535d71524ea786d77cceb2b2dc639df508
Author: Rob Pike <r@golang.org>
Date:   Wed Mar 18 14:05:05 2009 -0700

    simplify test to eliminate now-deprecated forms of switch.
    
    R=rsc
    DELTA=76  (0 added, 63 deleted, 13 changed)
    OCL=26439
    CL=26490

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

https://github.com/golang/go/commit/08eeb2535d71524ea786d77cceb2b2dc639df508

元コミット内容

simplify test to eliminate now-deprecated forms of switch. (現在非推奨となったswitchの形式を排除するためにテストを簡素化する。)

変更の背景

このコミットは2009年3月に行われており、Go言語がまだ一般に公開される前の、活発な開発と設計の初期段階に位置します。当時のGo言語は、その構文やセマンティクスが頻繁に実験され、洗練されていました。特に、型による分岐を扱うswitch文の構文は、いくつかの試行錯誤を経て現在の形に落ち着きました。

このコミットの背景には、Go言語の設計者たちが、より明確で、意図が分かりやすく、かつ効率的な型スイッチの構文を確立しようとしていたという経緯があります。初期のGo言語には、現在のswitch x := f(i).(type)という専用の型スイッチ構文とは異なる、あるいはそれを補完するような形で型による分岐を実現する試みが存在しました。しかし、言語の安定化と標準化の過程で、これらの初期の、あるいは実験的な構文の一部が非推奨とされ、最終的には削除されることになりました。

このコミットは、そのような非推奨となったswitchの形式をテストコードから削除することで、テストスイートを簡素化し、Go言語の進化と現在の標準的な構文に合わせることを目的としています。これにより、テストコードの保守性が向上し、言語の設計意図がより明確に反映されるようになりました。

前提知識の解説

Go言語の型スイッチ (Type Switch)

Go言語において、interface{}型(空インターフェース)は任意の型の値を保持できる特別な型です。しかし、interface{}型の変数が実際にどのような型の値を保持しているかを実行時に知る必要がある場合があります。このような場合に用いられるのが「型アサーション(Type Assertion)」と「型スイッチ(Type Switch)」です。

  • 型アサーション: value, ok := interfaceVar.(Type) の形式で、interfaceVarが特定のTypeであるかどうかをチェックし、もしそうであればその型に変換します。okは変換が成功したかどうかを示す真偽値です。
  • 型スイッチ: switch v := interfaceVar.(type) { ... } の形式で、interfaceVarが保持する具体的な型に基づいて異なるコードブロックを実行します。これは、複数の型に対する分岐を簡潔に記述できるため、一連のif-else ifと型アサーションを連ねるよりも推奨されます。

例:

func printType(i interface{}) {
	switch v := i.(type) {
	case int:
		fmt.Printf("Integer: %d\n", v)
	case string:
		fmt.Printf("String: %s\n", v)
	default:
		fmt.Printf("Unknown type: %T\n", v)
	}
}

Go言語の初期開発と構文の変遷

Go言語は、GoogleでRob Pike、Ken Thompson、Robert Griesemerによって設計され、2009年11月にオープンソースとして公開されました。このコミットが行われた2009年3月は、まさにその公開直前の時期にあたります。この期間は、言語のコア機能や構文が活発に議論され、実装され、そして時には変更される「揺籃期」でした。

言語設計の初期段階では、様々なアイデアが試され、その中には最終的に採用されなかったり、より洗練された形に置き換えられたりする構文も存在します。switch文、特に型による分岐のメカニズムも、この時期に現在の効率的で慣用的な形式へと進化していきました。このコミットは、その進化の過程で「非推奨」と判断された初期の構文をクリーンアップする一環として行われたものです。

技術的詳細

このコミットで「非推奨」とされたswitchの形式は、主に以下のパターンを指します。

  1. type guard style (型ガードスタイル): 削除されたコードブロックに示されているように、switch v := f(i); true { ... } のように、switch文の条件式にtrueを置き、その内部のcase節でx := v.(Type)のような型アサーションを行う形式です。

    // 削除されたコードの例
    // for i := Bool; i < Last; i++ {
    //     switch v := f(i); true { // ここがポイント: switch true
    //     case x := v.(bool):      // case節で型アサーション
    //         assert(x == true && i == Bool, "switch 1 bool");
    //     case x := v.(int):
    //         assert(x == 7 && i == Int, "switch 1 int");
    //     // ... 他の型 ...
    //     default:
    //         assert(false, "switch 1 unknown");
    //     }
    // }
    

    この構文は、現在のGo言語の型スイッチ switch x := f(i).(type) { ... } が導入される前の、型による分岐を実現するための初期の試みでした。switch trueと型アサーションを組み合わせることで、型に応じた処理を記述していましたが、これは冗長であり、型スイッチの本来の意図を直接的に表現していませんでした。

  2. catch-all style in various forms (様々な形式のキャッチオールスタイル): これもまた、switch trueを用いた一般的なswitch文のテストコードであり、特定の条件がtrueである場合に実行されることを確認していました。

    // 削除されたコードの例
    // switch true {
    // case x := m["6"]:(...) // map参照
    // case x := <-dummyc:(...) // channel受信
    // }
    

    これらのパターンも、Go言語の構文が洗練される過程で、より直接的で意図が明確な表現に置き換えられたか、あるいはテストの目的上不要になったものと考えられます。

なぜ非推奨になったのか?

Go言語の設計哲学の一つに「明示的であること」と「簡潔であること」があります。switch trueと型アサーションを組み合わせる形式は、型による分岐という特定の目的のために、より専用の、より簡潔な構文(switch v := expr.(type))が導入されたことで、その役割を終えました。新しい型スイッチ構文は、コードの可読性を高め、開発者が型による分岐を行っていることを一目で理解できるようにします。

この変更は、Go言語の構文を統一し、学習コストを下げ、将来的な言語の進化の基盤を固める上で重要なステップでした。テストコードからこれらの非推奨構文を削除することは、言語の進化を反映させ、テストスイートが常に最新の言語仕様に準拠していることを保証する意味合いがあります。

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

変更は test/typeswitch.go ファイルに集中しています。

  • 削除:
    • type guard style とラベル付けされた、switch v := f(i); true { ... } 形式の大きなコメントアウトされたコードブロックが完全に削除されました。これは、60行以上のコードに相当します。
    • catch-all style in various forms とラベル付けされた、switch true { ... } 形式のコメントアウトされたコードブロックが削除されました。これには、型ガード、マップ参照、チャネル操作を含む複数のテストケースが含まれていました。
  • 変更:
    • 既存の type switch style のテストブロック(switch x := f(i).(type) { ... })において、assert関数の第3引数(メッセージ文字列)が簡素化されました。例えば、"switch 2 bool" から "bool" のように、冗長なプレフィックスが削除されています。これは機能的な変更ではなく、テストメッセージのクリーンアップです。
    • 同様に、boolean switch のテストブロックでもassertメッセージが簡素化されています。

具体的な変更行は以下の通りです(+は追加、-は削除、変更は両方で表現されます)。

--- a/test/typeswitch.go
+++ b/test/typeswitch.go
@@ -35,7 +35,6 @@ func assert(b bool, s string) {
 	}\n }\n \n-\n func f(i int) interface{} {\n  	switch i {\n  	case Bool:\n@@ -61,64 +60,37 @@ func f(i int) interface{} {\n }\n \n func main() {\n-\t// type guard style
-//\tfor i := Bool; i < Last; i++ {\n-//\t\tswitch v := f(i); true {\n-//\t\tcase x := v.(bool):\n-//\t\t\tassert(x == true && i == Bool, \"switch 1 bool\");\n-//\t\tcase x := v.(int):\n-//\t\t\tassert(x == 7 && i == Int, \"switch 1 int\");\n-//	... (中略) ...
-//\t\t}\n-//\t}\n-\n-\t// type switch style
 \tfor i := Bool; i < Last; i++ {\n \t\tswitch x := f(i).(type) {\n \t\tcase bool:\n-\t\t\tassert(x == true && i == Bool, \"switch 2 bool\");\n+\t\t\tassert(x == true && i == Bool, \"bool\");\n \t\tcase int:\n-\t\t\tassert(x == 7 && i == Int, \"switch 2 int\");\n+\t\t\tassert(x == 7 && i == Int, \"int\");\n \t\tcase float:\n-\t\t\tassert(x == 7.4 && i == Float, \"switch 2 float\");\n+\t\t\tassert(x == 7.4 && i == Float, \"float\");\n \t\tcase string:\n-\t\t\tassert(x == \"hello\" && i == String, \"switch 2 string\");\n+\t\t\tassert(x == \"hello\"&& i == String, \"string\");\n \t\tcase S:\n-\t\t\tassert(x.a == 1234 && i == Struct, \"switch 2 struct\");\n+\t\t\tassert(x.a == 1234 && i == Struct, \"struct\");\n \t\tcase chan int:\n-\t\t\tassert(x == c && i == Chan, \"switch 2 chan\");\n+\t\t\tassert(x == c && i == Chan, \"chan\");\n \t\tcase []int:\n-\t\t\tassert(x[3] == 3 && i == Array, \"switch 2 array\");\n+\t\t\tassert(x[3] == 3 && i == Array, \"array\");\n \t\tcase map[string]int:\n-\t\t\tassert(x == m && i == Map, \"switch 2 map\");\n+\t\t\tassert(x == m && i == Map, \"map\");\n \t\tcase func(i int) interface{}:\n-\t\t\tassert(x == f && i == Func, \"switch 2 fun\");\n+\t\t\tassert(x == f && i == Func, \"fun\");\n \t\tdefault:\n-\t\t\tassert(false, \"switch 2 unknown\");\n+\t\t\tassert(false, \"unknown\");\n \t\t}\n \t}\n \n-\t// catch-all style in various forms
+\t// boolean switch (has had bugs in past; worth writing down)
 \tswitch {\n \tcase true:\n-\t\tassert(true, \"switch 3 bool\");\n+\t\tassert(true, \"switch 2 bool\");\n \tdefault:\n-\t\tassert(false, \"switch 3 unknown\");\n+\t\tassert(false, \"switch 2 unknown\");\n \t}\n \n \tswitch true {\n@@ -135,39 +107,4 @@ func main() {\n \t\tassert(false, \"switch 4 unknown\");\n \t}\n \n-//\tswitch true {\n-//\tcase x := f(Int).(float):\n-//	... (中略) ...
-//\t}\n-\n-\tm[\"7\"] = 7;\n-//\tswitch true {\n-//	... (中略) ...
-//\t}\n-\n-\tgo func() { <-c; c <- 77; } ();\n-//	... (中略) ...
-//\t}\n-\n }\n```

## コアとなるコードの解説

このコミットの核心は、Go言語の`switch`文の進化を反映したテストコードのクリーンアップです。

削除された`type guard style`のコードは、以下のようなパターンでした。

```go
// 削除された旧形式の型ガードスタイル
switch v := f(i); true { // f(i)の結果を変数vに代入し、switchの条件は常にtrue
case x := v.(bool):      // case節でvがbool型であるかをアサートし、xに代入
    // ... bool型の場合の処理 ...
case x := v.(int):       // case節でvがint型であるかをアサートし、xに代入
    // ... int型の場合の処理 ...
// ...
}

この形式は、switch文の一般的な条件分岐のメカニズムを流用して型による分岐を実現しようとしたものです。switch trueとすることで、各case節が独立した条件式(ここでは型アサーション)を持つかのように振る舞います。しかし、これは型による分岐という特定の目的には冗長であり、直感的ではありませんでした。

Go言語はその後、型による分岐のためにより専用の、現在のtype switch構文を導入しました。

// 現在の標準的な型スイッチ構文
switch x := f(i).(type) { // f(i)の結果の型に基づいて分岐し、その値をxに代入
case bool:
    // ... bool型の場合の処理 ...
case int:
    // ... int型の場合の処理 ...
// ...
}

この新しい構文は、.(type)という特別な構文を使用することで、変数の型に基づいて直接分岐できるため、より簡潔で、コードの意図が明確になります。このコミットは、この新しい、より優れた構文が確立されたことを受け、古いtype guard styleのテストコードを削除し、テストスイートを現在の言語仕様に合わせるためのものです。

また、その他のswitch trueを用いたコメントアウトされたテストコードの削除も、言語の進化に伴い、それらのテストケースが不要になったか、あるいはより適切な方法でテストされるようになったことを示唆しています。

assertメッセージの簡素化は、機能的な変更ではなく、テストの出力を見やすくするための軽微な改善です。

関連リンク

参考にした情報源リンク

  • Go言語のコミット履歴 (GitHub): https://github.com/golang/go/commits/master
  • Go言語のリリースノート (特に初期のバージョン): https://go.dev/doc/devel/release
  • Go言語の設計に関する議論(Go Wikiなど、当時の情報源)
    • Go Wiki - TypeSwitch: https://go.dev/wiki/TypeSwitch (現在の情報ですが、歴史的背景も示唆されています)
    • Go言語の初期の設計ドキュメントやメーリングリストのアーカイブ(一般にはアクセスしにくい場合が多いですが、言語の進化を理解する上で重要です)