Interface の謎

今回も軽めの小ネタで。

まず,文字列の配列を表示するだけの単純なコードを書いてみる。

package main

import "fmt"

func main() {
    strlist := []string{"alpha", "beta", "gamma"}
    fmt.Println(strlist)
}

結果は

[alpha beta gamma]

となる。 配列1 の中身をそのままダンプ出力しているだけなので,まぁ当たり前っちゃあ当たり前。 では,配列のダンプではなくきちんと項目を列挙したいとしよう。 やり方は色々あるが簡単に “...” トークンを使って

package main

import "fmt"

func main() {
    strlist := []string{"alpha", "beta", "gamma"}
    fmt.Println(strlist...)
}

と配列を展開すればいんじゃね? って思うよね,普通。 fmt.Println() 関数の定義を見ても

func Println(a ...interface{}) (n int, err error)

となっているし,問題ないように見える。

でもこれはうまくいかない。 これを実行しようとすると

prog.go:7: cannot use strlist (type []string) as type []interface {} in argument to fmt.Println

とエラーになる。

実は []string 型の strlistfmt.Println() 関数に渡す際に []interface{} 型ではなく interface{} 型に必ずキャストされる。 だから strlist... と展開しようとしても「そりゃあ無理(←超意訳)」と怒られてしまうわけだ。 Go 言語の型(type)は

type Msg []string

のように配列やポインタも型として定義できてしまうことを思い出して欲しい。

じゃあ,明示的なキャストならいけるのかと思ったが

package main

import "fmt"

type Msg []string

func main() {
    strlist := []string{"alpha", "beta", "gamma"}
    fmt.Println(([]interface{})(strlist)...)
}

結果は

prog.go:9: cannot convert strlist (type []string) to type []interface {}

と,これもエラーになった。

ではどうすればいいのかというと []interface{} 型の配列を用意してそこに値をコピーする。 先程のコードであれば

package main

import "fmt"

type Msg []string

func main() {
    strlist := []string{"alpha", "beta", "gamma"}
    var list = make([]interface{}, 0)
    for _, str := range strlist {
        list = append(list, str)
    }
    fmt.Println(list...)
}

とすれば

alpha beta gamma

のようにめでたく列挙される。 この問題は fmt.Println() 関数だけじゃなく,ある型の配列を []interface{} 型にキャストしようとする際は必ず発生するようだ2

いや,「“cannot use strlist (type []string) as type []interface {} in argument to fmt.Println” なんてコンパイルエラーを出せるならコンパイラ側でなんとかしてよ」と思うのだが,どうも無理らしい。

やれやれ。

ブックマーク

Go 言語に関するブックマーク集はこちら


  1. 厳密には slice。分かってますよ,もちろん。 ↩︎

  2. 例えば liststrlist の内容をコピーする際に for 文で回すのではなく list = append(list, strlist...) でできるかどうか試してみればいい。 ↩︎