Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Pointers 指针与参数和返回值中的值_Pointers_Go - Fatal编程技术网

Pointers 指针与参数和返回值中的值

Pointers 指针与参数和返回值中的值,pointers,go,Pointers,Go,在Go中,有多种方法返回struct值或其切片。对于我所看到的个别问题: type MyStruct struct { Val int } func myfunc() MyStruct { return MyStruct{Val: 1} } func myfunc() *MyStruct { return &MyStruct{} } func myfunc(s *MyStruct) { s.Val = 1 } 我理解它们之间的区别。第一个函数返回结

在Go中,有多种方法返回
struct
值或其切片。对于我所看到的个别问题:

type MyStruct struct {
    Val int
}

func myfunc() MyStruct {
    return MyStruct{Val: 1}
}

func myfunc() *MyStruct {
    return &MyStruct{}
}

func myfunc(s *MyStruct) {
    s.Val = 1
}
我理解它们之间的区别。第一个函数返回结构的副本,第二个函数返回指向函数中创建的结构值的指针,第三个函数期望传入现有结构并重写该值

我看到所有这些模式都在不同的环境中使用,我想知道关于这些模式的最佳实践是什么。你什么时候用哪个?例如,第一种方法适用于小型结构(因为开销最小),第二种方法适用于大型结构。第三,如果您希望非常高效地使用内存,因为您可以在调用之间轻松地重用单个结构实例。什么时候使用哪种方法有什么最佳实践

同样,关于切片的相同问题:

func myfunc() []MyStruct {
    return []MyStruct{ MyStruct{Val: 1} }
}

func myfunc() []*MyStruct {
    return []MyStruct{ &MyStruct{Val: 1} }
}

func myfunc(s *[]MyStruct) {
    *s = []MyStruct{ MyStruct{Val: 1} }
}

func myfunc(s *[]*MyStruct) {
    *s = []MyStruct{ &MyStruct{Val: 1} }
}

再次强调:这里的最佳实践是什么。我知道片总是指针,所以返回指向片的指针是没有用的。但是,我是否应该返回一个结构值片,一个指向结构的指针片,是否应该将指向一个片的指针作为参数(在中使用的模式)传入?

tl;博士

  • 使用接收器指针的方法很常见,如果有疑问,请使用指针
  • 片、映射、通道、字符串、函数值和接口值在内部用指针实现,指向它们的指针通常是冗余的
  • 在其他地方,对大型结构或必须更改的结构使用指针,否则,因为通过指针意外更改内容会令人困惑

您应该经常使用指针的一种情况:

  • 接收者比其他参数更经常是指针。方法修改它们被调用的对象,或者命名类型为大型结构,因此除了极少数情况外,默认为指针,这并不罕见。
    • Jeff Hodges的工具会自动搜索通过值传递的非微小接收器。
一些不需要指针的情况:

  • 代码审查指南建议将小型结构类型点结构{lation,longitude float64},甚至可能更大的东西作为值传递,除非您调用的函数需要能够在适当的位置修改它们

    • 值语义避免了这里的赋值意外更改那里的值时出现别名的情况
    • 牺牲干净的语义来换取一点速度并非易事,有时按值传递小结构实际上更有效,因为它避免了内存分配或堆分配
    • 所以,Go Wiki的页面建议在结构很小且可能保持这种状态时按值传递
    • 如果“大”界限看起来模糊,那么它就是;可以说,许多结构都在一个范围内,指针或值都可以。作为一个下限,代码审查评论建议使用切片(三个机器字)作为值接收者是合理的。作为更接近上限的东西,
      bytes.Replace
      需要10个单词的参数(三个片段和一个
      int
      )。您可以找到复制大型结构会带来性能提升的地方,但经验法则并非如此。
  • 对于切片,不需要传递指针来更改数组的元素<例如,code>io.Reader.Read(p[]字节)更改
    p
    的字节。这可以说是“像对待值一样对待小结构”的一个特例,因为在内部传递的是一个称为切片头的小结构(请参阅)。类似地,您不需要指向修改地图或在频道上进行通信的指针

  • 对于切片,您将重新切片(更改的开始/长度/容量),内置函数(如
    append
    )接受切片值并返回新值。我会模仿它;它避免了别名,返回一个新的切片有助于提醒人们注意可能会分配一个新的数组,调用者对此很熟悉

    • 遵循这种模式并不总是切实可行的。有些工具喜欢或需要附加到编译时类型未知的切片。它们有时接受一个指向
      接口{}
      参数中某个片的指针
  • 映射、通道、字符串以及函数和接口值与切片一样,都是内部引用或已经包含引用的结构,因此如果您只是试图避免复制底层数据,则不需要向它们传递指针。(rsc)

    • 在您想要修改调用者的结构的罕见情况下,您可能仍然需要传递指针:例如,出于这个原因,需要一个
      *字符串
使用指针的位置:

  • 考虑函数是否应该是需要指针指向的结构上的方法。人们期望在
    x
    上有很多方法来修改
    x
    ,因此,将修改后的结构作为接收者可能有助于将意外降至最低。当接收器应该是指针时,存在开启

  • 对非接收者参数有影响的函数应该在godoc中明确这一点,或者更好的是在godoc和名称中明确这一点(比如
    reader.WriteTo(writer)

  • 您提到通过允许重用来接受指针以避免分配;为了内存重用而更改API是一种优化,我会推迟,直到明确分配具有非平凡的成本,然后我会寻找一种不会将更复杂的API强加给所有用户的方法:

  • 为了避免分配,Go's是你的朋友。有时,您可以通过创建可以使用普通构造函数、纯文本或有用的零值(如)初始化的类型来帮助它避免堆分配
  • 考虑使用
    Reset()
    方法将对象放回blan中