この Codelab では、Goの標準ライブラリのひとつである reflect パッケージを学びます。
以下のゴールを達成して、Goでメタプログラミングができるようになりましょう!
reflect パッケージは、Goの標準ライブラリのひとつです。 reflect パッケージを使うと、プログラムは、自身を鏡に反射(reflection)するように、ランタイムの型や値を見ることができるようになります。
そして、型や値を見るだけだなく、ランタイムでそれらを変更することができます。
ランタイムの型の情報を取得するには reflect.TypeOf()を使用します。
取得できる型情報は Type interface を実装しています。
package main
import (
"fmt"
"reflect"
)
type person struct {
name string
}
func main() {
p := person{
name: "にゅも太郎",
}
t := reflect.TypeOf(p)
fmt.Println(t.Name()) // person
}
ランタイムの値の情報を取得するには reflect.ValueOf()を使用します。
取得できる値の情報は Value struct を実装しています。
package main
import (
"fmt"
"reflect"
)
type person struct {
name string
}
func main() {
p := &person{
name: "にゅも太郎",
}
v := reflect.ValueOf(p)
fmt.Println(v.Elem()) // {にゅも太郎}
}
ある struct A を、別の struct Bにコピーすることを考えます。 もっともシンプルな方法は、すべてのフィールドに対して代入をおこなうことです。
package main
import (
"fmt"
)
type Person struct {
Name string
Age int
}
func main() {
p1 := Person{Name: "Alice", Age: 20}
p2 := Person{}
copyPerson(&p1, &p2)
fmt.Println(p2) // {Alice 20}
}
func copyPerson(src, dst *Person) {
dst.Name = src.Name
dst.Age = src.Age
}
この方法には、以下のような問題があります。
Person のフィールドが増えると copyPerson のコードを変更する必要があるreflect パッケージを使ってこの問題を解決してみましょう。
任意の型の struct をコピーできる copyStruct 関数を実装してみましょう。 雛形のコードはこちらです。
package main
import (
"fmt"
"reflect"
)
type Person struct {
Name string
DNA string
Soul string `copyable:"false"`
}
type Food struct {
Name string
Kind string
secretIngredient string
}
func main() {
p1 := Person{Name: "Alice", DNA: "ALICE_DNA", Soul: "ALICE_SOUL"}
p2 := Person{}
f1 := Food{Name: "Icecream", Kind: "Sweet", secretIngredient: "Salt"}
f2 := Food{}
copyStruct(&p1, &p2)
copyStruct(&f1, &f2)
fmt.Println(p2) // {Alice 20}
fmt.Println(f2) // {Icecream Sweet}
}
// TODO: implement
func copyStruct() {}
any を使いましょうreflect.ValueOf() を使いましょうValue.Elem() を使いますValue.NumField() を使うと、フィールドの数を取得できますValue.Filed(index) を使うと、その index のフィールドを取得できますValue.CanSet() を使いましょうValue.Set(Value) を使うと、値情報を別の値情報にマッピングできますフィールドが copyable:"false" の Struct Tag をもつ場合、代入をスキップしてみましょう。
reflect パッケージを使ったメタプログラミングには、ランタイムの振る舞いを変える以外にも、以下のように使うことができます。 関連のOSSのソースコードなどを読んでみましょう。
go generate を使って、コード生成する