英文:
Should I create pointers on struct field or on struct? Go
问题
我想知道指针的最佳实践是什么。我应该在结构体上定义它们还是在其字段上定义它们。我认为定义一个指向结构体本身的指针是有道理的,但是这里有一个我觉得很有趣的例子。如果所有的字段都是指针,为什么我不应该使用一个指向整个结构体的指针来获取每个字段的地址呢?
以下是结构体内容的示例:
{
"tag": "v0.0.1",
"sha": "940bd336248efae0f9ee5bc7b2d5c985887b16ac",
"url": "https://api.github.com/repos/octocat/Hello-World/git/tags/940bd336248efae0f9ee5bc7b2d5c985887b16ac",
"message": "initial version\n",
"tagger": {
"name": "Scott Chacon",
"email": "schacon@gmail.com",
"date": "2011-06-17T14:53:35-07:00"
},
"object": {
"type": "commit",
"sha": "c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c",
"url": "https://api.github.com/repos/octocat/Hello-World/git/commits/c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c"
}
}
英文:
I'm wondering what's the best practice on pointers. Should I define them on the struct or on its fields. I though it makes sense to define a pointer to the struct itself but here is an example I find intriguing. If all the fields are pointers why shouldn't I use a pointer to the entire struct instead to get an address for each field?
type Tag struct {
Tag *string `json:"tag,omitempty"`
SHA *string `json:"sha,omitempty"`
URL *string `json:"url,omitempty"`
Message *string `json:"message,omitempty"`
Tagger *CommitAuthor `json:"tagger,omitempty"`
Object *GitObject `json:"object,omitempty"`
}
A sample of the struct content below
{
"tag": "v0.0.1",
"sha": "940bd336248efae0f9ee5bc7b2d5c985887b16ac",
"url": "https://api.github.com/repos/octocat/Hello-World/git/tags/940bd336248efae0f9ee5bc7b2d5c985887b16ac",
"message": "initial version\n",
"tagger": {
"name": "Scott Chacon",
"email": "schacon@gmail.com",
"date": "2011-06-17T14:53:35-07:00"
},
"object": {
"type": "commit",
"sha": "c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c",
"url": "https://api.github.com/repos/octocat/Hello-World/git/commits/c3d0be41ecbe669545ee3e94d31ed9a4bc91ee3c"
}
}
答案1
得分: 3
在这种情况下,使用指针字段确实更高效,但他们在这里使用指针的原因有点奇怪,可以在博客文章“Go, REST APIs, and Pointers”中讨论。
你所说的结构体似乎在go-github库的这里定义。它将每个字段都定义为指针,这样就可以轻松地对任何字段的子集传递nil
(只需不指定它们)。这样,当你构造一个PATCH
调用来通过GitHub API更新某个内容时,你可以指定Description
是否与你的请求无关(你不更新描述),或者你是否打算将Description
设置为""
。关键是,在PATCH
调用中,""
和nil
具有不同的含义。
如果你有类似的需求,要区分零字符串/结构等与“不适用于此对象”,你也可以使用指针。但如果你不需要这样做,最好不要将每个字段都定义为指针,因为这样会导致内存使用模式变差——占用更多的RAM,更多的缓存未命中,GC需要跟踪更多的内容等等。一种不添加指针间接层的方法(尽管在编写代码时看起来稍微冗长一些)是sql.NullString
,它只是一个带有bool和string的结构体。
在GitHub的情况下,它可能对性能产生的影响并不是很大——GitHub响应Web请求所花费的时间将远远超过其库执行的任何CPU密集型工作。
英文:
It is more efficient to have non-pointer fields, but in this case they have an odd reason to use pointers, discussed at the blog post "Go, REST APIs, and Pointers".
It looks like the struct you're talking about is defined here, in the go-github library. It makes every field a pointer so that it's trivial to pass nil
for any subset of fields (just don't specify them). That way when you're constructing, say, a PATCH
call to update something via the GitHub API, you can specify whether Description
is just not relevant to your request (you're not updating the description) or whether you intend to set Description
to ""
. The key thing is that ""
and nil
have different meanings in PATCH
calls to their API.
If you have a similar desire to distinguish a zero string/struct/etc. from "not applicable to this object", you can also use pointers. If you don't need that, though, it's better not to make every field a pointer, because that will tend to make your memory usage patterns worse--little more RAM taken up, more cache misses, more stuff the GC needs to trace through, etc. An approach that doesn't add that layer of pointer indirection (but looks a tiny bit more verbose when writing code) is sql.NullString
, which is just a struct with a bool and a string.
In GitHub's case, any performance impact of it isn't a huge deal--the time GitHub takes to respond to the Web request is going to dwarf any CPU-bound work their library does anyway.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论