How to modify the value of a simple type through pointer receiver method in Go?

ぐ巨炮叔叔 提交于 2021-01-28 19:20:29

问题


I wanted to have a custom type based on a basic type and be able to set its value by calling a pointer receiver.

When I run the following program:

package main

import (
    "fmt"
    "strconv"
)

type FooInt int
func (fi *FooInt) FromString(i string) {
    num, _ := strconv.Atoi(i)
    tmp := FooInt(num)
    fi = &tmp
}

func main() {
    var fi *FooInt
    fi.FromString("5")
    fmt.Printf("%v\n", fi)
}

I receive <nil>. Why doesn't the pointer declared in main() change its value to the address of tmp?

Here is a Go playground link.


回答1:


All arguments–including the receiver–is a copy inside the function/method. You can only modify the copy.

This applies to pointers too: the receiver value (the fi pointer) is a copy, so you can't modify the original pointer, only the copy.

Usually the receiver is a non-nil pointer, and you modify the pointed value–which results in the original pointed value changed.

In your case you either have to return the pointer and assign the return value:

func (fi *FooInt) FromString(i string) *FooInt {
    num, _ := strconv.Atoi(i)
    tmp := FooInt(num)
    return &tmp
}

func main() {
    var fi *FooInt
    fi = fi.FromString("5")
    fmt.Printf("%v %v\n", fi, *fi)
}

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

0xc0000b4020 5

Or pass a non-nil pointer to what you want to change, in your case it would be of type **FooInt

func (fi *FooInt) FromString(i string, p **FooInt) {
    num, _ := strconv.Atoi(i)
    tmp := FooInt(num)
    *p = &tmp
}

func main() {
    var fi *FooInt
    fi.FromString("5", &fi)
    fmt.Printf("%v %v\n", fi, *fi)
}

This outputs the same. Try it on the Go Playground.

But easiest would be to just ensure the receiver is not nil, so the pointed value can simply be modified:

func (fi *FooInt) FromString(i string) {
    num, _ := strconv.Atoi(i)
    *fi = FooInt(num)
}

func main() {
    var fi *FooInt
    fi = new(FooInt)
    fi.FromString("5")
    fmt.Printf("%v %v\n", fi, *fi)
}

Output is the same. Try this one on the Go Playground.




回答2:


The syntax:

func (fi *FooInt) FromString(i string) {
    // ...
}

is partly syntactic sugar for:

func FromString(fi *fooInt, i string) {
    // ...
}

That is, the fi parameter here is an ordinary local variable. If you assign to it, you replace the pointer value that the caller supplied, rather than writing through the pointer value that the caller supplied. Hence you need to use:

    *fi = FooInt(num)

in the body of the function. However, now the caller must pass a non-nil pointer:

var fi FooInt
fi.FromString("5")

for instance.

Here is a complete example, including a method by which you can call the FromString function and pass an explicit pointer.

(I say partly syntactic sugar because this defines FromString as a receiver function or method, which can only be done using this syntax. So the syntax is required—it's not an alternative to some other syntax, as people sometimes mean when using the phrase "syntactic sugar".)



来源:https://stackoverflow.com/questions/61761945/how-to-modify-the-value-of-a-simple-type-through-pointer-receiver-method-in-go

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!