I created a custom type based on the Golang net.IP
type. What surprised me is that a method declared with a pointer receiver to my custom type can't modify the value to which the receiver points.
The u
variable in this code snippet remains nil
after calling u.defaultIP()
. The IP can be modified if I changed my custom type to a struct with an IP field and the method is defined with a pointer receiver to the struct. What am I missing? Executable example can be found here.
type userIP net.IP
func main() {
var u *userIP
u.defaultIP()
fmt.Printf("%v\n", u)
}
func (u *userIP) defaultIP() {
defaultIP := userIP("127.0.0.1")
u = &defaultIP
}
You can declare methods with pointer receivers. This means the receiver type has the literal syntax *T for some type T . (Also, T cannot itself be a pointer such as *int .) For example, the Scale method here is defined on *Vertex .
Introduction. A method is just a function with a special receiver type between the func keyword and the method name. The receiver can either be a struct type or non-struct type. The syntax of a method declaration is provided below. func (t Type) methodName(parameter list) { }
There are two reasons to use a pointer receiver. The first is so that the method can modify the value that its receiver points to. The second is to avoid copying the value on each method call. This can be more efficient if the receiver is a large struct, for example.
you can mix and match methods with value receivers and methods with pointer receivers, and use them with variables containing values and pointers, without worrying about which is which. Both will work, and the syntax is the same.
You need to dereference the u
before setting it's value.
From your example, change
defaultIP := userIP("127.0.0.1")
u = &defaultIP
to
*u = userIP("127.0.0.1")
For your example updated and working: https://play.golang.org/p/ycCLT0ed9F
TL;DR: The pointer receiver needs to be dereferenced before it's value can be set. This applies to both struct and non-struct types. In the case of struct types, the dereferencing is automatically done by the selector expression.
After digging around a bit further, I think this behaviour is caused by the fact that the pointer receiver is not the same pointer calling the method.
Running this code snippet shows that the u
pointer in the main()
function is different from that in the defaultIP()
method. Essentially, I end up only modifying the u
pointer in the defaultIP()
method. Executable example can be found here.
func main() {
var u *userIP
u.defaultIP()
fmt.Printf("main(): address of pointer is %v\n", &u)
fmt.Printf("main(): user IP address is %v\n", u)
}
type userIP net.IP
func (u *userIP) defaultIP() {
defaultIP := userIP("127.0.0.1")
u = &defaultIP
fmt.Printf("defaultIP(): address of pointer is %v\n", &u)
fmt.Printf("defaultIP(): user IP address is %s\n", *u)
}
The correct way to do this is as pointed in Tom's answer i.e. dereference u
in the defaultIP()
method.
What puzzled me earlier was why would this example work if I wrapped the IP as a field in the struct? Running the code snippet shows that the two u
pointers are indeed different, but the ip
field is modified. Executable example can be found here.
func main() {
u := &userInfo{}
u.defaultIP()
fmt.Printf("main(): address of pointer is %v\n", &u)
fmt.Printf("main(): user IP address is %s\n", u.ip)
}
type userInfo struct{
ip net.IP
}
func (u *userInfo) defaultIP() {
u.ip = net.ParseIP("127.0.0.1")
fmt.Printf("defaultIP(): address of pointer is %v\n", &u)
fmt.Printf("defaultIP(): user IP address is %s\n", u.ip)
}
Turns out that this is caused by the selector expression (x.y
). To quote the doc,
Selectors automatically dereference pointers to structs. If x is a pointer to a struct, x.y is shorthand for (x).y; if the field y is also a pointer to a struct, x.y.z is shorthand for ((*x).y).z, and so on. If x contains an anonymous field of type *A, where A is also a struct type, x.f is a shortcut for (*x.A).f.
So in my case, the u.ip
expression dereferences u
before modifying the ip
field, which essentially translates to(*u).ip
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With