7

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
}

3 Answers 3

8

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

Sign up to request clarification or add additional context in comments.

2 Comments

dereference* (I tried editing this but it didn't allow me since the change was less than 6 characters)
The reader needs to click on a link to understand this answer. (unless you already know what dereference means, but even so, how do you do that in golang—click on the link to find out :D
2

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.

Comments

0

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.

3 Comments

Using u = &defaultIP is my problem. Without the dereferenced *u, userIP{127, 0, 0, 1} won't work.
userIP("127.0.0.1") is fine. Refer Tom's answer at play.golang.org/p/ycCLT0ed9F.
@isim You may use net.ParseIP("127.0.0.1") or userIP{127, 0, 0, 1}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.