LoginSignup
0
0

Generics 関数の中で制約付きのポインタ型と実体型を両方扱いたい

Last updated at Posted at 2023-10-07

Go1.20 の頃の話なんですけど。

お困りごと

こんな感じでレシーバがポインタで宣言されてて、かつ関数の中でレシーバのフィールドを触ってるケース。

type Hogeable interface {
	Hoge()
}

type HogeUser1 struct{ s string }
type HogeUser2 struct{ s string }
type HogeUser3 struct{ s string }

func (h *HogeUser1) Hoge() {
	h.s = "HogeUser1"
	fmt.Println(h.s)
}
func (h *HogeUser2) Hoge() {
	h.s = "HogeUser2"
	fmt.Println(h.s)
}
func (h *HogeUser3) Hoge() {
	h.s = "HogeUser3"
	fmt.Println(h.s)
}

これを実体宣言して呼び出す Generics 関数を用意したい、となったとき。
ナイーブに Generics にすると、T がそもそもポインタ型なので実体が作れなくて死んでしまう。

func BadCallHoge[T Hogeable]() {
	var hoge T
	hoge.Hoge()
}

func main() {
	BadCallHoge[*HogeUser1]()
	BadCallHoge[*HogeUser2]()
	BadCallHoge[*HogeUser3]()
}
ぬるぽ
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x108ddb8]

ちなみに型引数に実体型を渡すとコンパイルエラーになる。レシーバがポインタで宣言されてるため。

BadCallHoge[HogeUser1]()
// HogeUser1 does not satisfy Hogeable (method Hoge has pointer receiver)

改善策

実体型を any で渡して、それに interface で制約つけて、キャストして呼び出してやるといけるらしい。

func CallHoge[T any, PT interface {
	Hogeable
	*T
}]() {
	var hoge T
	PT(&hoge).Hoge()
}

func main() {
	CallHoge[HogeUser1]()
	CallHoge[HogeUser2]()
	CallHoge[HogeUser3]()
}

ちなみに any のところは union でもおk、というか union で絞った方がいいかも。
T HogeUser1 | HogeUser2 | HogeUser3

なお、これが正しいやり方なのかはわからん。

全体のコード

package main

import (
	"fmt"
)

type Hogeable interface {
	Hoge()
}

type HogeUser1 struct{ s string }
type HogeUser2 struct{ s string }
type HogeUser3 struct{ s string }

func (h *HogeUser1) Hoge() {
	h.s = "HogeUser1"
	fmt.Println(h.s)
}
func (h *HogeUser2) Hoge() {
	h.s = "HogeUser2"
	fmt.Println(h.s)
}
func (h *HogeUser3) Hoge() {
	h.s = "HogeUser3"
	fmt.Println(h.s)
}

func CallHoge[T any, PT interface {
	Hogeable
	*T
}]() {
	var hoge T
	PT(&hoge).Hoge()
}

func main() {
	CallHoge[HogeUser1]()
	CallHoge[HogeUser2]()
	CallHoge[HogeUser3]()
}

おわり。

0
0
0

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
0
0