Patipol Thamdee
Patipol Thamdee

Reputation: 33

Confusing of using pointer in Go

I have my sample code like below.

type Apple struct {
    Color string
}
//In this way, the code runs just fine.
func main(){
        var test = 6
        TestTest(&test)
        fmt.Println(test)
        a := Apple{"red"}
        Eat(&a)
        fmt.Println(a.Color)
}


    func TestTest(num *int) {
        *num = *num + 2
    }
    func Eat(a *Apple) {
        a.Color = "green"
    }

the question is, why do I have to put a star(*) before a num variable but not for a.Color? If I do so to a.Color, it says

invalid indirect of a.Color (type string)

or if I remove a star(*) from num, it says

invalid operation: num + 2 (mismatched types *int and int)

that makes me confused, can someone explain why?

Upvotes: 2

Views: 1814

Answers (2)

Himanshu
Himanshu

Reputation: 12685

These are two different cases:

Case1

num is a pointer to int, So you need to add the integer value to the value stored at address pointed by num. Hence you are dereferencing the num pointer to get the value stored inside it as:

func TestTest(num *int) {
    *num = *num + 2 // dereference the pointer to get the value.
}

Case2

you are assigning a string value to Color field of Apple struct which is not a pointer. But you are using the pointer to the struct not to the field. That's why you are able to assign a value like this:

func Eat(a *Apple) { // a is the pointer to struct.
    a.Color = "green"
}

Now if you want to generate the same error which is you are getting in first case, create a pointer type Color field inside the struct as:

type Apple struct {
    Color *string // this is the pointer value which will throw the same error in your current implementation.
}

Error code on Go playground when trying to assign a pointer type value to a non-pointer variable when using struct.

Solution

To set the value in case you are using pointer field in a struct use reflection as:

package main

import (
    "fmt"
    "reflect"
)

//I have my sample code like this.

type Apple struct {
    Color *string
}

//In this way, the code runs just fine.
func main() {
    var test = 6
    TestTest(&test)
    fmt.Println(test)
    point := "red"
    a := Apple{&point}
    Eat(&a)
    fmt.Println(a.Color)
}

func TestTest(num *int) {
    *num = *num + 2
}
func Eat(a *Apple) {
    str := "green"
    r := reflect.ValueOf(a)
    elm := r.Elem().FieldByName("Color")
    elm.Set(reflect.ValueOf(&str))
    fmt.Printf("%+v", (*a).Color)
}

Playground Example

One more thing to notice is the value of reflection is actually reflect.Ptr so what we can do is we can loop over the struct fields to get the value and then use reflect.Indirect to get the value of the pointer type color field.

func Eat(a *Apple) {
    str := "green"
    r := reflect.ValueOf(a).Elem()
    elm := r.FieldByName("Color")
    elm.Set(reflect.ValueOf(&str))
    fmt.Printf("%+v\n", (*a).Color)
    for i := 0; i < r.NumField(); i++ {
        valueField := r.Field(i)
        fmt.Println(reflect.Indirect(valueField))
    }
    // or use FieldByName to get the value of a field.
    st := "Color"
    fmt.Println(reflect.Indirect(reflect.ValueOf(a).Elem().FieldByName(st)))
}

Upvotes: 7

SwiftMango
SwiftMango

Reputation: 15294

Because Go automatically dereferences a pointer before dot property.

You can also do it manually by:

(*a).Color = "green"

Your code failed probably because you didn't use the parenthese and it tries to dereference Color property instead of a itself.

Upvotes: 5

Related Questions