在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
}
我了解两者之间的区别。第一个返回该结构的副本,第二个返回指向在函数内创建的结构值的指针,第三个期望传入现有结构并覆盖该值。
我已经看到所有这些模式都可以在各种情况下使用,我想知道关于这些的最佳实践是什么。什么时候使用?例如,第一个可能适用于小型结构(因为开销很小),第二个适用于较大的结构。第三,如果您想提高内存效率,因为您可以轻松地在调用之间重用单个结构实例。有什么最佳实践,何时使用?
同样,关于 slice 的相同问题:
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} }
}
再说一遍:什么是最佳实践。我知道 slice 始终是指针,因此返回指向 slice 的指针没有用。但是,是否应该返回一个结构值 slice ,一个指向结构的指针 slice ,是否应该将指向 slice 的指针作为参数传递(在Go App Engine API中使用的模式)?
最佳答案
tl;博士:
一种应该经常使用指针的情况:
在一些不需要指针的情况下:
type Point struct { latitude, longitude float64 }
,甚至传递一些更大的值作为值,除非您调用的函数需要能够在适当的位置修改它们。bytes.Replace
接受了值(value)10个字的args(三个 slice 和一个int
)。您可以找到situations,即使复制较大的结构也可以赢得性能,但是经验法则并非如此。 io.Reader.Read(p []byte)
更改p
的字节。可以说这是“对待像值一样的小结构”的特例,因为在内部,您正在传递一个称为 slice 头的小结构(请参见Russ Cox (rsc)'s explanation)。同样,您不需要指向的指针即可修改 map 或在 channel 上进行通信。append
这样的内置函数接受 slice 值并返回一个新值。我会模仿的;它避免了混淆,返回一个新的分片有助于引起人们注意可能分配了一个新数组的事实,并且调用者很熟悉它。interface{}
参数中接受指向 slice 的指针。 flag.StringVar
为此需要*string
。 使用指针的位置:
x
上的许多方法来修改x
,因此使修改后的结构成为接收器可能有助于最大程度地减少意外。当接收者应该是指针时有guidelines。reader.WriteTo(writer)
)中使之清楚。bytes.Buffer
)初始化的类型来帮助它避免堆分配。 Reset()
方法将对象放回空白状态。不在乎或无法保存分配的用户不必调用它。 existingUser.LoadFromJSON(json []byte) error
可以用NewUserFromJSON(json []byte) (*User, error)
包装。再次,它在懒惰和捏分配给单个调用者之间进行选择。 sync.Pool
处理一些细节。如果特定的分配产生了很大的内存压力,您可以确定何时不再使用该分配,并且没有更好的优化方法,sync.Pool
可以为您提供帮助。 (CloudFlare发布了有关回收的a useful (pre- sync.Pool
) blog post。)最后,关于 slice 是否应为指针:值 slice 可以很有用,并且可以节省分配和缓存未命中。可能有阻止者:
NewFoo() *Foo
而不是让Go使用zero value初始化。 append
在grows the underlying array时复制项目。在append
之前指向的指针指向错误的位置之后,对于大型结构(例如,对于禁止复制sync.Mutex
。在中间插入/删除并类似地移动项目。 广义上讲,如果您将所有项目都放在适当的位置并且不移动它们(例如,在初始设置后不再移动
append
),或者如果您确实继续移动它们,则可以确定值(value) slice 是否合理没关系(无需/谨慎使用指向项目的指针,项目足够小以至于无法有效复制等)。有时您必须考虑或衡量具体情况,但这只是一个粗略的指导。关于pointers - 指针与参数和返回值中的值,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/23542989/