【问题标题】:Golang Non-Struct Type Pointer ReceiverGolang 非结构类型指针接收器
【发布时间】:2016-08-24 03:21:19
【问题描述】:

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

此代码 sn-p 中的 u 变量在调用 u.defaultIP() 后仍为 nil。如果我将自定义类型更改为具有 IP 字段的结构并且该方法是使用指向该结构的指针接收器定义的,则可以修改 IP。我错过了什么?可执行示例可以在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
}

【问题讨论】:

    标签: go


    【解决方案1】:

    在设置它的值之前,您需要取消引用 u

    从你的例子中,改变

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

    *u = userIP("127.0.0.1")
    

    对于您的示例更新和工作:https://play.golang.org/p/ycCLT0ed9F

    【讨论】:

    • 取消引用*(我尝试编辑此内容,但由于更改少于 6 个字符,因此无法编辑)
    • 读者需要点击链接才能理解这个答案。 (除非你已经知道取消引用是什么意思,但即便如此,你如何在 golang 中做到这一点——点击链接了解:D
    【解决方案2】:

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

    在进一步挖掘之后,我认为这种行为是由于指针接收器不是调用该方法的同一个指针。

    运行这段代码sn-p可以看出main()函数中的u指针与defaultIP()方法中的指针不同。本质上,我最终修改了defaultIP() 方法中的u 指针。可执行示例可以在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)
    }
    

    执行此操作的正确方法是 Tom 的回答中指出的,即在 defaultIP() 方法中取消引用 u

    之前让我感到困惑的是,如果我将 IP 包装为结构中的一个字段,为什么这个示例会起作用?运行代码sn -p可以看出,这两个u指针确实不一样,只是修改了ip字段。可执行示例可以在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)
    }
    

    原来这是由selector expression (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

    【讨论】:

      【解决方案3】:

      两个选项:

      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 Docs:

      IP 是一个单一的 IP 地址,一个字节片。这里面的功能 包接受 4 字节 (IPv4) 或 16 字节 (IPv6) 切片作为输入。

      【讨论】:

      • 使用u = &defaultIP 是我的问题。如果没有取消引用的*uuserIP{127, 0, 0, 1} 将无法工作。
      • userIP("127.0.0.1") 很好。参考汤姆的回答play.golang.org/p/ycCLT0ed9F
      • @isim 您可以使用net.ParseIP("127.0.0.1")userIP{127, 0, 0, 1}
      猜你喜欢
      • 1970-01-01
      • 2014-07-30
      • 1970-01-01
      • 1970-01-01
      • 2015-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-09-22
      相关资源
      最近更新 更多