Reputation: 9268
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
}
Upvotes: 7
Views: 2129
Reputation:
Two Options:
1- With dereferencing: like this working code and using net.ParseIP("127.0.0.1")
(The Go Playground):
package main
import (
"fmt"
"net"
)
type userIP net.IP
func main() {
var u userIP
u.defaultIP()
fmt.Println(u)
}
func (u *userIP) defaultIP() {
*u = userIP(net.ParseIP("127.0.0.1"))
}
output:
[0 0 0 0 0 0 0 0 0 0 255 255 127 0 0 1]
2- Without dereferencing (The Go Playground):
package main
import (
"fmt"
"net"
)
type userIP net.IP
func main() {
u := make(userIP, 4)
u.defaultIP()
fmt.Printf("%v\n", u)
}
func (u userIP) defaultIP() {
u[0], u[1], u[2], u[3] = 127, 0, 0, 1
}
And note that net.IP
is []byte
, see net.IP
Docs:
An IP is a single IP address, a slice of bytes. Functions in this package accept either 4-byte (IPv4) or 16-byte (IPv6) slices as input.
Upvotes: 0
Reputation: 113
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
Upvotes: 8
Reputation: 9268
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
.
Upvotes: 2