在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} }
}
再说一遍:什么是最佳实践。我知道切片始终是指针,因此返回指向切片的指针没有用。但是,我应该返回一个结构值切片,一个指向结构的指针切片,是否应该将指向切片的指针作为参数传递(Go
App Engine
API中
使用的模式)?
tl; dr :
一种应该经常使用指针的情况:
接收器 比其他参数更经常地使用指针。方法修改被调用的东西或命名类型为大型结构并不罕见,因此,在极少数情况下,指南是默认使用指针。
在一些不需要指针的情况下:
代码审查指南建议将 小结构( 如type Point struct { latitude, longitude float64 }
,甚至可能更大的东西)作为值传递,除非您调用的函数需要能够就地对其进行修改。
bytes.Replace
需要使用10个单词的args(三个切片和一个int
)。 对于 slices ,您不需要传递指针来更改数组的元素。例如,io.Reader.Read(p []byte)
更改的字节p
。可以说这是“对待像值一样的小结构”的特例,因为在内部,您正在传递一个称为 切片头 的小结构(请参阅Russ Cox(rsc)的说明)。同样,您也不需要指针来 修改地图或在channel上进行通信 。
对于 切片,您将 进行 切片 (更改其开始/长度/容量),内置函数,例如append
接受切片值并返回一个新值。我会模仿的;它避免了混淆,返回一个新的分片有助于引起人们注意可能分配了一个新数组的事实,并且调用者很熟悉它。
interface{}
参数中切片的指针。映射,通道,字符串以及函数和接口值 (例如切片)是内部引用或已经包含引用的结构,因此,如果您只是试图避免复制基础数据,则无需将指针传递给它们。(rsc 撰写了有关如何存储接口值的单独文章)。
您可能仍然需要通过指针在罕见的情况下,要 修改 调用者的结构:flag.StringVar
需要一个*string
出于这个原因,例如。
使用指针的位置:
考虑您的函数是否应该是您需要指向的任何结构上的方法。人们期望有很多方法可以x
进行修改x
,因此使接收器成为修改后的结构可能有助于最大程度地减少意外。对于何时应该将接收者作为指针有一些指导。
对非接收器参数有影响的函数应该在godoc中,或者更好的是,在godoc和名称(如reader.WriteTo(writer)
)中明确指出。
您提到接受一个指针,以允许通过重用避免分配。为了内存重用而更改API是一种优化,我会延迟直到明显知道分配费用不菲,然后再寻找一种不会对所有用户强制使用棘手API的方法:
bytes.Buffer
。Reset()
将对象放回空白状态的方法,例如某些stdlib类型提供的方法。不在乎或无法保存分配的用户不必调用它。existingUser.LoadFromJSON(json []byte) error
可以考虑编写就地修改方法和从头创建函数作为匹配对,以方便使用:可以用进行包装NewUserFromJSON(json []byte) (*User, error)
。再次,它在懒惰和捏分配给单个呼叫者之间做出选择。sync.Pool
处理一些细节。如果特定的分配产生了很大的内存压力,您有信心知道何时不再使用该分配,并且没有更好的优化方法可以提供sync.Pool
帮助。(CloudFlare发表了sync.Pool
关于回收的有用(上)博客文章。)最后,关于切片是否应该是指针:值切片可以很有用,并且可以节省分配和缓存未命中。可能有阻止者:
NewFoo() *Foo
而不是让Go初始化为零值。append
在增长基础数组时会复制项目。指向append
错误位置之后的指针,对于大型结构,复制可能会变慢,并且例如,sync.Mutex
不允许复制。在中间插入/删除并类似地移动项目。从广义上讲,如果您将所有物品放在前面就位并且不移动它们(例如,append
在初始设置后不再移动),或者如果您确实一直在移动它们,但是您确定可以(无需/小心使用指向项目的指针,项目足够小以至于无法有效复制等)。有时您必须考虑或衡量具体情况,但这只是一个粗略的指导。
在Go中,有多种方法可以返回< code>struct值或其片段。对于我见过的个别例子: 我理解它们之间的区别。第一个返回结构的副本,第二个是指向在函数中创建的结构值的指针,第三个期望传入现有结构并覆盖该值。 我看到所有这些模式都在不同的环境中使用,我想知道关于这些模式的最佳实践是什么。你什么时候用?例如,第一个可以用于小结构(因为开销最小),第二个可以用于大结构。第三,如果你想非常节省内存,因为
2. 指针类型的参数和返回值 首先看以下程序: 例 23.1. 指针参数和返回值 #include <stdio.h> int *swap(int *px, int *py) { int temp; temp = *px; *px = *py; *py = temp; return px; } int main(void) { int i = 10, j = 20; int *p
如果指针类型作为函数参数,修改形参会影响实参 不能将函数内的指向局部变量的指针作为返回值,函数结束指向空间会被释放 可以将函数内的局部变量作为返回值,本质是拷贝一份
我在玩C语言中的函数指针只是为了学习。我尝试调用一个空函数,并将其结果设置为int。 我从中得到的结果是: 在对它进行了一点研究之后,我意识到main正在函数()中打印printf语句中的字符数。为什么会这样?这是预期产出吗?
问题内容: 在Go中,如何将函数调用返回的值分配给指针? 考虑下面的示例,注意返回一个值(不是指针): 这些都失败了: 是否确实需要局部变量?那不会招致不必要的复制吗? 问题答案: 根据规范,必须使用局部变量。 要获取值的地址,调用函数必须将返回值复制到可寻址的内存中。有副本,但这不是多余的。 Go程序通常使用值。 有时在应用程序想要区分无值和其他时间值的情况下使用A。在SQL NULL和有效时间
函数能够接收参数供自己使用,也可以返回零个或多个值(我们通常把返回多个值称为返回一组值)。相比与 C、C++、Java 和 C#,多值返回是 Go 的一大特性,为我们判断一个函数是否正常执行(参考 第 5.2 节)提供了方便。 我们通过 return 关键字返回一组值。事实上,任何一个有返回值(单个或多个)的函数都必须以 return 或 panic(参考 第 13 章)结尾。 在函数块里面,re