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

0f1d16574e6d15530b0e9e9b837d1d86?s=47 morikuni
December 18, 2018

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

0f1d16574e6d15530b0e9e9b837d1d86?s=128

morikuni

December 18, 2018
Tweet

Transcript

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

  2. About me • morikuni • https://twitter.com/inukirom • https://github.com/morikuni • Mercari

    Microservices Development • Go & Application Architecture • mercari.go に来て!
  3. 絶対にExportされてないフィールドを書き換える なよ!絶対だぞ!絶対!

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

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

  6. 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} }
  7. 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 }
  8. まとめ • Exportされていないフィールドを書き換えました • 絶対に本番環境ではつかうなよ!絶対だぞ!絶対!