-1

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

    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)
}
1

1 Answer 1

3

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:""}
Sign up to request clarification or add additional context in comments.

2 Comments

Amazing, thanks for your solution! Just for my understanding, var foo *Foo is already a pointer, right? I was expecting reflect to be able to modify the value that that pointer points to. But if I understand what you're saying correctly, the function receives a copy of that pointer, which it cannot update?
@minitauros Yes, that's right. The function could modify the pointed object, but not the pointer stored in the foo variable–unless you pass the address of the foo variable itself.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.