Slide 1

Slide 1 text

絶対にExportされてないフィールドを書き換える なよ!絶対だぞ!絶対! golang.tokyo #20

Slide 2

Slide 2 text

About me ● morikuni ● https://twitter.com/inukirom ● https://github.com/morikuni ● Mercari Microservices Development ● Go & Application Architecture ● mercari.go に来て!

Slide 3

Slide 3 text

絶対にExportされてないフィールドを書き換える なよ!絶対だぞ!絶対!

Slide 4

Slide 4 text

「あれ?Exportされてないフィールドってそ  もそも書き換えられないよね?何言ってる   の?」 Goの有識者の声

Slide 5

Slide 5 text

書き換えに成功して しまいまいした。 大変申し訳ありませんが github.com/morikuni/go-experiment/overwrite

Slide 6

Slide 6 text

Usage import ( "testing" "github.com/morikuni/go-experiment/overwrite" "github.com/morikuni/go-experiment/overwrite/internal" ) func TestField(t *testing.T) { x := internal.NewX("aaa") overwrite.Field(&x, "val", "bbb") if got, want := x.Get(), "bbb"; got != want { t.Errorf("got %v, want %v", got, want) } } package internal type X struct { val string } func (x X) Get()string { return x.val } func NewX(a string) X{ return X{a} }

Slide 7

Slide 7 text

Implementation func Field(target interface{}, field string, val interface{}) error { targetVal := reflect.Indirect(reflect.ValueOf(target)) // セットするstructのreflect.Valueをとる if targetVal.Kind() != reflect.Struct { return ErrNotStruct } dstVal := targetVal.FieldByName(field) // セットするフィールドのreflect.Valueをとる if !dstVal.IsValid() { return ErrNoSuchField } srcVal := reflect.ValueOf(val) // セットする値のreflect.Valueをとる if srcVal.Type() != dstVal.Type() { return ErrTypeMismatch } dstAddr := unsafe.Pointer(dstVal.UnsafeAddr()) // セットするフィールドのアドレスをとる setableField := reflect.NewAt(dstVal.Type(), dstAddr).Elem() // セットするフィールドのアドレスにval型の変数を作成する setableField.Set(srcVal) // setableFieldはただの変数なので値をセットできる return nil }

Slide 8

Slide 8 text

まとめ ● Exportされていないフィールドを書き換えました ● 絶対に本番環境ではつかうなよ!絶対だぞ!絶対!