4
0

More than 1 year has passed since last update.

Go generics で指定された型のオブジェクトをインターフェイスとして返す

Last updated at Posted at 2022-07-17

これは何?

最近 Go を書く機会があって。

generics 関数内で引数型のインスタンスを作れなくて苦労したので、やりたかったこと、書いてみて駄目だったこと、うまく行ったコードを記す。

やりたいこと

インターフェイス Hoge があり。Hoge を実装するクラスが FugaType 他多数あり。

go
hogeCreator := CreateHogeCreator[FugaType](arg)
hoge := hogeCreator()

のように使う generics な関数 CreateHogeCreator が書きたかった。

トライアル

まずは素朴に

go1.18
func CreateHogeCreator[T Hoge](arg HogeInitArg) func() Hoge {
	return func() Hoge {
		v := T{} // invalid composite literal type T compiler(InvalidLit)
		v.Init(arg)
		return v
	}
}

と書くとエラー。T は インターフェイスかもしれないんだから、そりゃ作れない。

T がなにかのポインタであるべきなのかなにかであるべきなのかもごっちゃになっている。駄目。
しかしその辺りを整理して、呼ぶ側の調整をしても全然うまくいかない。

必要なのは「Hoge を実装している構造体(インターフェイスではない)」という制約。そうじゃないと T{} って書けない。

正解

正解はこう。

go1.18
type HogeConstraint[X any] interface {
	Hoge
	*X
}

func CreateHogeCreator[T any, PT HogeConstraint[T]](arg HogeInitArg) func() Hoge {
	return func() Hoge {
		var v T
		pv := PT(&v)
		pv.Init(arg) // (&v).Init(arg) だとエラー
		return pv    // return &v だとエラー
	}
}

こんなの思いつかないよ。

この定義で、 hogeCreator := CreateHogeCreator[FugaType](arg) と呼べるようになる。

ちなみに。
単にファクトリ関数なら hoge := CreateHoge(&FooType{}, arg) のようにすればそもそも generics が不要になる。

しかし。
CreateHogeCreator のように、generics 内で型引数で指定された型のインスタンスを作りたいことはまあまああって、その場合このようなコードが必要になる。

まとめ

go の generics、今のところすごく難しい。
私の理解が足りないからそう感じるのか、本当に難しいのかはまだわからない。

下記の記事のおかげで書けた。ありがとうございます。

4
0
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0