Goクイズアドベントカレンダー25日目
December 25, 2020
この記事は、Goクイズ Advent Calendar 2020の25日目の記事です。まずはこちらの記事を読んでください。
問題
以下のコードをThe go2go Playgroundで実行するとどうなるか?なお、型パラメタの仕様はType Parameters - Draft Designに従う。
type Setter interface { Set(int) }
type Settable struct { n int }
func (p *Settable) Set(n int) { p.n = n }
func FromInt[T Setter](n int) T {
var v T
v.Set(n)
return v
}
func main() {
fmt.Println(FromInt[*Settable](100))
}
- コンパイルエラー
{0}
と表示される{100}
と表示される- パニックが発生
解答と解説
答えは「4. パニックが発生」でした!
解答
FromInt
関数の型引数を*Settable
としているため、FromInt
関数内の型パラメタT
は、*Settable
型になります。そのため、変数v
のゼロ値はnil
となり、Set
メソッド内のp.n = n
でパニックが発生します。
以下のように、ゼロ値がnil
にならないよう型引数にSettable
を指定すれば良さそうですが、そうするとSettable
がSet
メソッドを実装していないため、コンパイルエラーとなります。
func main() {
fmt.Println(FromInt[Settable](100))
}
この問題を解決するためには、以下のように任意の型を表す型パラメタとそのポインタを表す型パラメタを定義すると良いでしょう。Setter
インタフェースを実装する型はポインタであることを指定するため、型リストtype *B
を追加しています。Setter
インタフェースもジェネリックな型として定義し、任意の型のポインタであることを表現しています。
FromInt
関数では、T any, PT Setter[T]
のように型パラメタを設けることにより、型パラメタT
は任意の型、型パラメタPT
はそのポインタを表しています。そうすることにより、nil
ではないゼロ値を得られる上に、そのポインタを取得することにより、Set
メソッドを呼び出せます。
type Setter[B any] interface {
Set(int)
type *B
}
func FromInt[T any, PT Setter[T]](n int) T {
var v T
PT(&v).Set(n)
return v
}
インスタンス化する場合は、型推論が働くため、以下のように型パラメタT
に対応するSettable
型だけ指定すれば良いでしょう。
func main() {
fmt.Println(FromInt[Settable](100))
}
このように型引数を指定することにより、型パラメタPT
は以下のように推論されます。
{ T, PT }
{ T -> Settable, PT -> *T }
{ T -> Settable, PT -> *Settable }
複雑な型パラメタは少し難しいですが、新しい機能はワクワクしますね!それでは良いお年を。