interface conversion panic の原因と直し方
型アサーション v.(T) は、vの実際の型がTでない場合に実行時パニックを起こします。2つ目の戻り値(ok)を受け取らずに単一戻り値の形で書くと、失敗時にそのままパニックします。
エラーメッセージの読み方
panic: interface conversion: interface {} is string, not int
panic- パニック — 実行時に回復不能な状態になったことを示します。deferとrecoverで捕まえない限り、そのままプロセスが終了します
interface conversion: interface {} is string, not int- 詳細メッセージ — どの値が問題だったか
このエラーが出る典型パターン
パターン1
1 package main 2 3 import "fmt" 4 5 func main() { 6 var i interface{} = "hello" ^ 7 n := i.(int) 8 fmt.Println(n) 9 }
単一戻り値の形の型アサーションi.(int)は、iの実際の型がintでなければその場でパニックします。
直し方: "hello" を 42 にします。
パターン2
1 package main 2 3 import "fmt" 4 5 func main() { 6 var i interface{} = true ^ 7 n := i.(int) 8 fmt.Println(n) 9 }
boolを格納したinterfaceに対してintへの型アサーションを行うと、実際の型がbool, not intとしてパニックします。
直し方: true を 42 にします。
パターン3
1 package main 2 3 import "fmt" 4 5 func main() { 6 var i interface{} = 3.14 ^ 7 n := i.(int) 8 fmt.Println(n) 9 }
数値同士に見えてもfloat64とintは別の型で、暗黙の変換はされず型アサーションは失敗します。
直し方: 3.14 を 42 にします。
パターン4
1 package main 2 3 import "fmt" 4 5 func main() { 6 var i interface{} = []int{1, 2} ^ 7 n := i.(int) 8 fmt.Println(n) 9 }
スライスをintに変換しようとするような明らかな型違いでも、コンパイルは通ってしまい実行時に発覚します。
直し方: []int{1, 2} を 42 にします。
パターン5
1 package main 2 3 import "fmt" 4 5 func main() { 6 var i interface{} = int64(5) ^ 7 n := i.(int) 8 fmt.Println(n) 9 }
int64とintは同じ「整数」でもGoでは別の型として区別され、型アサーションはint64, not intとして失敗します。
直し方: int64(5) を 42 にします。
よくある誤解
「型が違っていても、アサーションはゼロ値を返してくれるはず」という思い込みは誤りです。単一戻り値の形の型アサーションは失敗時にパニックするため、失敗しうる場合はv, ok := v.(T)の形でokを確認する必要があります。
実務での勘所
2つ目の戻り値でokを確認するこの書き方は、型アサーションだけの特別な構文ではありません。v, ok := m[key](マップの読み取り)やv, ok := <-ch(チャネルの受信)でも、まったく同じ「値と、それが有効だったかを示す真偽値」というペアの形が使われています。Goはこの「comma ok」と呼ばれる形を複数の異なる言語機能にわたって一貫して採用しており、1つの場面で覚えれば他の場面でも同じ考え方が通用します。