Golang非结构类型指针接收器

huangapple go评论86阅读模式
英文:

Golang Non-Struct Type Pointer Receiver

问题

我根据Golang的net.IP类型创建了一个自定义类型。令我惊讶的是,使用指针接收器声明的方法无法修改接收器指向的值。

在这段代码中,调用u.defaultIP()后,u变量仍然是nil。如果我将自定义类型更改为具有IP字段的结构体,并且该方法使用指针接收器定义在结构体上,那么IP可以被修改。我错过了什么?可在这里找到可执行的示例。

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
}
英文:

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
}

答案1

得分: 8

你需要在设置u的值之前对其进行解引用。

根据你的示例,将代码修改为:

defaultIP := userIP("127.0.0.1")
u = &defaultIP

修改为:

*u = userIP("127.0.0.1")

你可以查看更新后的示例代码并测试:https://play.golang.org/p/ycCLT0ed9F

英文:

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

答案2

得分: 2

TL;DR:在设置值之前,需要对指针接收器进行解引用。这适用于结构体和非结构体类型。对于结构体类型,解引用是由选择器表达式自动完成的。

经过进一步的调查,我认为这种行为是由于指针接收器与调用方法的指针不同所导致的。

运行这段代码片段显示,在main()函数中的u指针与defaultIP()方法中的指针是不同的。实质上,我最终修改了defaultIP()方法中的u指针。可在此处找到可执行的示例。

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)
}

正确的做法是如Tom的答案所指出的,在defaultIP()方法中对u进行解引用。

之前让我困惑的是,如果我将IP封装为结构体中的字段,为什么这个示例会起作用?运行代码片段显示,两个u指针确实是不同的,但ip字段被修改了。可在此处找到可执行的示例。

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)
}

原来,这是由于选择器表达式x.y)引起的。引用文档的说法:

> 选择器自动解引用指向结构体的指针。如果x是指向结构体的指针,则x.y是(x).y的简写;如果字段y也是指向结构体的指针,则x.y.z是((x).y).z的简写,依此类推。如果x包含类型为A的匿名字段,其中A也是结构体类型,则x.f是(*x.A).f的简写。

因此,在我的情况下,u.ip表达式在修改ip字段之前对u进行了解引用,这实际上相当于(*u).ip

英文:

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.

答案3

得分: 0

两个选项:

1- 使用解引用:像这个工作代码一样,使用 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"))
}

输出结果:

[0 0 0 0 0 0 0 0 0 0 255 255 127 0 0 1]

2- 不使用解引用 (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
}

注意,net.IP[]byte 类型,参见 net.IP 文档:

IP 是一个单个 IP 地址,是一个字节切片。该包中的函数接受 4 字节(IPv4)或 16 字节(IPv6)的切片作为输入。

英文:

Two Options:

1- With dereferencing: like this working code and using net.ParseIP("127.0.0.1")
(The Go Playground):

<!-- language: lang-golang -->

package main

import (
	&quot;fmt&quot;
	&quot;net&quot;
)

type userIP net.IP

func main() {
	var u userIP
	u.defaultIP()
	fmt.Println(u)
}

func (u *userIP) defaultIP() {
	*u = userIP(net.ParseIP(&quot;127.0.0.1&quot;))
}

output:

[0 0 0 0 0 0 0 0 0 0 255 255 127 0 0 1]

2- Without dereferencing (The Go Playground):

<!-- language: lang-golang -->

package main

import (
	&quot;fmt&quot;
	&quot;net&quot;
)

type userIP net.IP

func main() {
	u := make(userIP, 4)
	u.defaultIP()
	fmt.Printf(&quot;%v\n&quot;, 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.

huangapple
  • 本文由 发表于 2016年8月24日 11:21:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/39113876.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定