[インデックス 1087] ファイルの概要
このコミットは、Go言語の型システムにおける誤用、具体的にはインターフェース型ではない値に対して型アサーション("type guard")を適用しようとしたバグを修正するものです。Go言語の初期段階において、インターフェース値から具体的な型を取り出した後に、その具体的な型に対してさらに型アサーションを連鎖させるという誤った記述がテストコード内に存在していました。このコミットは、その誤った型アサーションの連鎖を、適切な型変換に置き換えることで修正しています。
コミット
commit f8d7f5bd81e122a3ac13a176453d4ba810b07918
Author: Ian Lance Taylor <iant@golang.org>
Date: Fri Nov 7 11:44:15 2008 -0800
Don't use a type guard with a type which is not an interface.
R=r,gri
DELTA=2 (0 added, 0 deleted, 2 changed)
OCL=18781
CL=18785
---
test/fixedbugs/bug113.go | 2 +-\
test/ken/interbasic.go | 2 +-\
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/fixedbugs/bug113.go b/test/fixedbugs/bug113.go
index c3109a9cd2..ea75260cf2 100644
--- a/test/fixedbugs/bug113.go
+++ b/test/fixedbugs/bug113.go
@@ -13,7 +13,7 @@ func main() {
i = 1;
var v1 int = i;
if foo1(v1) != 1 { panicln(1) }\n- var v2 int32 = i.(int).(int32);\n+ var v2 int32 = int32(i.(int));\n if foo2(v2) != 1 { panicln(2) }\n var v3 int32 = i; // This implicit type conversion should fail at runtime.\n if foo2(v3) != 1 { panicln(3) }\ndiff --git a/test/ken/interbasic.go b/test/ken/interbasic.go
index e4b0b79a43..05eea395bb 100644
--- a/test/ken/interbasic.go
+++ b/test/ken/interbasic.go
@@ -86,7 +86,7 @@ main()\n \tu64 = 765432;\tia[12] = u64;\n \n \ts = ia[0];\tif s != \"xxx\" { panicln(0,s); }\n-\ti32 = ia[1].(int).(int32);\n+\ti32 = int32(ia[1].(int));\n \t\t\tif i32 != 12345 { panicln(1,i32); }\n \tb = ia[2];\tif b != true { panicln(2,b); }\n \n```
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/f8d7f5bd81e122a3ac13a176453d4ba810b07918](https://github.com/golang/go/commit/f8d7f5bd81e122a3ac13a176453d4ba810b07918)
## 元コミット内容
Don't use a type guard with a type which is not an interface.
R=r,gri DELTA=2 (0 added, 0 deleted, 2 changed) OCL=18781 CL=18785
## 変更の背景
Go言語の型システムでは、型アサーション `x.(T)` は、`x` がインターフェース型である場合にのみ有効です。これは、インターフェース値が内部に保持している具体的な値とその型を取り出すためのメカニズムです。しかし、このコミットが修正している問題は、インターフェース値から一度具体的な型(例: `int`)を取り出した後、その具体的な型に対してさらに型アサーションを連鎖させようとしていた点にあります。
具体的には、`i.(int).(int32)` のような記述が問題でした。ここで `i.(int)` は、インターフェース `i` が `int` 型の値を保持していることをアサートし、その結果として `int` 型の具体的な値が返されます。この `int` 型の値はもはやインターフェースではないため、その後に続く `.(int32)` という型アサーションは文法的に誤りであり、コンパイルエラーまたは実行時エラーを引き起こす可能性がありました。
このコミットは、Go言語の型システムの厳密なルールに則り、このような誤った型アサーションの連鎖を修正し、コードの健全性を保つことを目的としています。
## 前提知識の解説
このコミットを理解するためには、Go言語における以下の概念を理解しておく必要があります。
### 1. 型 (Types)
Go言語の型は、変数が保持できる値の種類を定義します。例えば、`int` は整数、`string` は文字列を表します。
### 2. インターフェース (Interfaces)
インターフェースは、メソッドのシグネチャの集合を定義する型です。Go言語のインターフェースは、JavaやC#のような明示的な`implements`キーワードを必要とせず、型がインターフェースのすべてのメソッドを実装していれば、そのインターフェースを満たすと見なされます(構造的型付け)。インターフェース型の変数は、そのインターフェースを満たす任意の具体的な型の値を保持できます。
例:
```go
type Reader interface {
Read(p []byte) (n int, err error)
}
type MyReader struct{}
func (mr MyReader) Read(p []byte) (n int, err error) {
// ...
}
var r Reader = MyReader{} // MyReader は Reader インターフェースを満たす
3. 型アサーション (Type Assertions)
型アサーション x.(T)
は、インターフェース値 x
が、特定の具体的な型 T
の値を保持しているかどうかをチェックし、もしそうであればその具体的な値を取り出すために使用されます。
型アサーションには2つの形式があります。
- 単一の値形式:
value := i.(T)
i
がT
型の値を保持していない場合、パニックが発生します。 - カンマOK形式:
value, ok := i.(T)
i
がT
型の値を保持している場合、value
にその値が代入され、ok
はtrue
になります。保持していない場合、value
はT
型のゼロ値になり、ok
はfalse
になります。この形式は、パニックを避けて型チェックを行うためによく使われます。
重要: 型アサーションは、インターフェース型の変数に対してのみ適用できます。具体的な型の変数に対して型アサーションを行おうとすると、コンパイルエラーになります。
4. 型変換 (Type Conversions)
型変換 T(x)
は、ある型の値 x
を別の型 T
の値に変換するために使用されます。これは、互換性のある型の間で行われます。例えば、int
を int32
に、またはその逆の変換などです。
例:
var i int = 10
var i32 int32 = int32(i) // int から int32 への型変換
技術的詳細
このコミットが修正している問題は、Go言語の型アサーションの誤用に関するものです。
元のコードでは、以下のような記述がありました。
i.(int).(int32)
ここで、i
はインターフェース型の変数であると仮定します(そうでなければ、最初の i.(int)
の時点でコンパイルエラーになります)。
-
i.(int)
: これは型アサーションです。インターフェースi
がint
型の値を保持していることをアサートし、そのint
型の具体的な値を取り出します。この操作の結果は、もはやインターフェース型ではなく、純粋なint
型の具体的な値になります。 -
(int).(int32)
: 問題はここにあります。i.(int)
の結果はint
型の具体的な値であり、これはインターフェースではありません。Go言語の仕様では、型アサーションx.(T)
はx
がインターフェース型である場合にのみ許可されます。したがって、具体的な型であるint
に対してさらに.(int32)
という型アサーションを適用しようとすることは、文法的に誤りです。
この誤った記述は、Go言語のコンパイラがまだ初期段階であり、このような特定のケースでのエラー検出が不十分であったか、あるいは言語仕様の解釈がまだ固まっていなかった時期に書かれたものと考えられます。
修正後のコードは以下のようになります。
int32(i.(int))
この修正は、Go言語の型システムに完全に準拠しています。
-
i.(int)
: これはこれまで通り、インターフェースi
からint
型の具体的な値を取り出す型アサーションです。結果はint
型の具体的な値です。 -
int32(...)
: ここで、int
型の具体的な値をint32
型に型変換しています。int
とint32
は互換性のある数値型であるため、この変換はGo言語のルールに従って正しく行われます。
この変更により、コードはGo言語の型システムに厳密に準拠し、意図しない動作やコンパイルエラーを回避できるようになります。これは、言語の成熟とコンパイラの堅牢化の過程で発見され、修正された典型的な例と言えます。
コアとなるコードの変更箇所
変更は2つのテストファイルで行われています。
test/fixedbugs/bug113.go
--- a/test/fixedbugs/bug113.go
+++ b/test/fixedbugs/bug113.go
@@ -13,7 +13,7 @@ func main() {
i = 1;
var v1 int = i;
if foo1(v1) != 1 { panicln(1) }\n- var v2 int32 = i.(int).(int32);\n+ var v2 int32 = int32(i.(int));\n if foo2(v2) != 1 { panicln(2) }\n var v3 int32 = i; // This implicit type conversion should fail at runtime.\n if foo2(v3) != 1 { panicln(3) }\ndiff --git a/test/ken/interbasic.go b/test/ken/interbasic.go
test/ken/interbasic.go
--- a/test/ken/interbasic.go
+++ b/test/ken/interbasic.go
@@ -86,7 +86,7 @@ main()\n \tu64 = 765432;\tia[12] = u64;\n \n \ts = ia[0];\tif s != \"xxx\" { panicln(0,s); }\n-\ti32 = ia[1].(int).(int32);\n+\ti32 = int32(ia[1].(int));\n \t\t\tif i32 != 12345 { panicln(1,i32); }\n \tb = ia[2];\tif b != true { panicln(2,b); }\n \n```
## コアとなるコードの解説
両方のファイルで、以下のパターンが修正されています。
**変更前:**
`X.(Type1).(Type2)`
* `X`: インターフェース型の変数(例: `i` や `ia[1]`)。
* `.(Type1)`: `X` が `Type1` 型の値を保持していることをアサートし、その具体的な `Type1` 型の値を取り出す。この結果はインターフェース型ではない。
* `.(Type2)`: `Type1` 型の具体的な値に対して、さらに `Type2` への型アサーションを行おうとしている。これは、型アサーションがインターフェース型にのみ適用可能であるというGoのルールに違反する。
**変更後:**
`Type2(X.(Type1))`
* `X.(Type1)`: インターフェース `X` から `Type1` 型の具体的な値を取り出す。ここまでは変更前と同じ。
* `Type2(...)`: `X.(Type1)` の結果である `Type1` 型の具体的な値を、`Type2` 型に**型変換**する。`Type1` と `Type2` が互換性のある型であれば、この操作はGoのルールに従って正しく行われる。
この修正は、Go言語の型システムにおける型アサーションと型変換の適切な使い分けを明確に示しています。インターフェースから具体的な値を取り出す際には型アサーションを使用し、異なる具体的な型間で値を変換する際には型変換を使用するという原則に基づいています。
## 関連リンク
* Go言語の仕様: [https://go.dev/ref/spec](https://go.dev/ref/spec)
* 型アサーションに関するセクション (Type assertions)
* 型変換に関するセクション (Conversions)
## 参考にした情報源リンク
* Go言語の公式ドキュメント
* Go言語の型システムに関する一般的な解説記事