💪

Go のリフレクション小ネタ

に公開

これは何?

Go の reflect パッケージを使って DI ライブラリを作ろうとしたときに知った小ネタです。

この記事の内容は Go 1.24 で実施しています。

構造体の型引数の型名を取得する方法

いきなりですが、reflect.Type から直接型引数を取得することはできないようです。proposal は出ているようですが、進んではいなそうでした。

https://github.com/golang/go/issues/54393

ですので、型引数を取得するためには reflect.TypeName() メソッドを使って型名を取得し、文字列をパースする必要があります。

package main

import (
	"fmt"
	"reflect"
)

type Foo[T1, T2, T3, T4 any] struct{}
type Bar struct{}

func main() {
	fmt.Println(reflect.TypeOf(Foo[Bar, *Bar, []Bar, int]{}).Name()) // Foo[main.Bar,*main.Bar,[]main.Bar,int]
}

型名の [] の部分に , 区切りで型名が入り、それぞれ {パッケージのパス}.{型名} という形式になっています。ポインタの場合はパッケージのパスの前に * 、配列の場合は [] がつきます。また、組込みデータ型はパッケージのパスはありません。

ちなみに、無名の構造体を使った場合は、定義全てが型名に含まれます。

package main

import (
	"fmt"
	"reflect"
)

type Foo[T any] struct{}

func main() {
	fmt.Println(reflect.TypeOf(Foo[struct{ Name string }]{}).Name()) // Foo[struct { Name string }]
}

インターフェースの reflect.Type を取得する方法

インターフェースの型を取得する場合、以下のように直接 reflect.TypeOf を使うと nil になってしまいます。

package main

import (
	"fmt"
	"reflect"
)

type Foo interface {
}

func main() {
	var f Foo
	fmt.Println(reflect.TypeOf(f).Name()) // panic: runtime error: invalid memory address or nil pointer dereference
}

インターフェースの型を取得する場合は、ポインタに対して reflect.TypeOf を使って .Elem() で実体を取り出す必要があります。

package main

import (
	"fmt"
	"reflect"
)

type Foo interface {
}

func main() {
	var f Foo
	fmt.Println(reflect.TypeOf(&f).Elem().Name()) // Foo
}

関数の型引数の型名は取得できない

まず、 関数名は runtime.FuncForPC を使って取得できます。

package main

import (
    "fmt"
    "reflect"
    "runtime"
)

func Foo() {
}

func main() {
	fmt.Println((runtime.FuncForPC(reflect.ValueOf(Foo).Pointer()).Name())) // main.Foo
}

型引数を持つ関数に対して同様の方法で関数名を取得しようとすると、型引数の部分は [...] という形式になってしまい取得できません。

package main

import (
	"fmt"
	"reflect"
	"runtime"
)

func Foo[T any]() {}

func main() {
	fmt.Println((runtime.FuncForPC(reflect.ValueOf(Foo[int]).Pointer()).Name())) // main.Foo[...]
}

これについては go/ast 等をつかってコード解析を行うしか取得する方法はなさそうでした。

まとめ

Go のリフレクションの小ネタでした。軽く調べて見つからなかったネタに絞ったら思ったより数が少なめでした...

最後に現在作成中のDIライブラリのリンクを貼っておきます。興味があれば見てみてください。

https://github.com/suger-131997/dein

Discussion