简体   繁体   中英

Set value of interface representing nil pointer using reflect in Go

My question is similar to How to initialize struct pointer via reflection .

I have an interface that represents a nil pointer. I want to update the value behind the nil pointer.

I took the code from one of the examples in the answers to the question mentioned above, but my situation is slightly different, and the given solution isn't working.

Inside InitNilPointer() the value actually does become &Foo{} . But the result at the call site remains (*Foo)(nil) . How can I use reflect to update foo in main() to &Foo{} ?

The situation is the following

func main() {
    var foo *Foo

    repository.InitNilPointer(foo)

    // Want: &main.Foo{A:""}
    fmt.Printf("Want: %+#v\n", &Foo{})

    // Got: (*main.Foo)(nil)
    fmt.Printf("Got: %+#v\n", foo)
}

// InitNilPointer initiates the given pointer to a nil value to become a pointer to 
// a value. E.g. (*Foo)(nil) becomes &Foo{}.
func InitNilPointer(source interface{}) {
    v := reflect.ValueOf(source)
    if reflect.Indirect(v).IsValid() {
        return
    }
    rv := reflect.ValueOf(&source).Elem()
    t := rv.Elem().Type().Elem()

    // This only updates the value locally. The call site still has a nil pointer. SAD.
    rv.Set(reflect.New(t))

    // source: &main.Foo{A:""}
    fmt.Printf("source: %+#v\n", source)
}

Whatever you pass to a function, a copy is made. If you pass foo , no matter what the function does, it can only modify the copy but not the original foo variable.

So you must pass &foo (and modifying the pointed object will modify foo ):

func main() {
    var foo *Foo

    InitNilPointer(&foo)

    fmt.Printf("Want: %+#v\n", &Foo{})
    fmt.Printf("Got: %+#v\n", foo)
}

func InitNilPointer(source interface{}) {
    rv := reflect.ValueOf(source).Elem()
    if reflect.Indirect(rv).IsValid() {
        return
    }
    t := rv.Type().Elem()

    rv.Set(reflect.New(t))

    // source: &main.Foo{A:""}
    fmt.Printf("source: %+#v\n", source)
}

This will output (try it on the Go Playground ):

source: (**main.Foo)(0xc00000e028)
Want: &main.Foo{A:""}
Got: &main.Foo{A:""}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM