问题
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