quick question; why pointer?

xuanbao · · 460 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I&#39;m coming from Java, and pointers are really not my strong side. So please bear with me. I&#39;m following a simple tutorial, and I&#39;ve noticed this code:</p> <p>//Page is a struct defined earlier</p> <p>func (p *Page) save() error { filename := p.Title + &#34;.txt&#34; return ioutil.WriteFile(filename, p.Body, 0600) }</p> <p>I mean, I would just implement it like this:</p> <p>func save() (p Page) error { filename := p.Title + &#34;.txt&#34; return ioutil.WriteFile(filename, p.Body, 0600) }</p> <p>(Yeah, I prefer the version where you have a regular parameter as input, instead of the receiver, but not my point). My question is, why do they parse a pointer to the Page, instead of the Page itself? Is it because it is computationally more efficient, since the pointer is what, a pointer to somewhere in memory, instead of the struct datatype? Or could someone briefly explain me why it wouldn&#39;t be equally good just to parse the page down as you normally would in a language like Java.</p> <hr/>**评论:**<br/><br/>tgulacsi: <pre><p>Go passes arguments by value, so if you want to modify a field in a struct, you&#39;ll need a pointer to the struct. Otherwise you&#39;d modify the field in a copy of the struct, so nobody&#39;d see the effect.</p></pre>shovelpost: <pre><p>EDIT: <del>Quick answer: Because you need to modify Page.Title.</del></p> <p><a href="https://github.com/golang/go/wiki/CodeReviewComments#receiver-type" rel="nofollow">Not so quick but more informed answer.</a></p></pre>leaf_bebop: <pre><p>The link is great but Save() does not modify anything.</p></pre>shovelpost: <pre><p>Oh you are right, my bad. I am gonna edit my post.</p></pre>robe_and_wizard_hat: <pre><p>In this example, they could have just used the value receiver <code>(p Page)</code>. That method kind of looks like this under the hood:</p> <pre><code>func save(p Page) error { ... } </code></pre> <p>So <code>p</code> is just a parameter, even if it is the receiver. Generally speaking, you want to use the value receiver, but if you need to mutate the <code>Page</code> in the method, you&#39;ll want to use a pointer receiver.</p> <p>There is some thought that it&#39;s more efficient to just pass pointers everywhere. I&#39;d like to discourage that. Copying memory is pretty fast (esp on the stack) and the read-only semantics you get from making a copy of the receiver is nice from a safety standpoint.</p> <p>My M.O. is to use value receivers unless I have to use a pointer receiver.</p></pre>chrillefar: <pre><p>Yeah, so first of all, to the part where i wrote &#34;func save() (p Page)...&#34;, it should clearly have been &#34;func save(p Page)&#34; haha, I&#39;m getting a bit tired as well. But yeah, so basically only use the pointers if there is a reason to, like if I actually wanna change the value of p, throughout the program. Thanks for the answer.</p></pre>GrandOpener: <pre><p>I don&#39;t agree with kjk&#39;s assessment of where bugs are likely, but his overall advice is correct and the downvotes seem unfair. The &#34;default&#34; choice, when you don&#39;t have a reason to prefer either, is a pointer receiver.</p> <p>This link was already given earlier in the thread by <a href="/u/shovelpost" rel="nofollow">/u/shovelpost</a>: it is both good advice and the canonically &#34;right&#34; answer: <a href="https://github.com/golang/go/wiki/CodeReviewComments#receiver-type" rel="nofollow">https://github.com/golang/go/wiki/CodeReviewComments#receiver-type</a></p></pre>kjk: <pre><p>Actually, passing structs by pointer should be your default and you should only pass by value if there&#39;s a good reason to.</p> <p>With correctness in mind, it&#39;s more frequent to have a bug because you thought you modified the struct in a function but you didn&#39;t (because you passed a copy) than the other way around.</p> <p>With performance in mind, passing by copy is expensive. Not for an individual copy but if you make copies for every function call, those will add up very quickly, especially if you call such a function in a loop.</p> <p>The executable will be bigger (because those instructions to allocate a new object and copy the data are not free).</p></pre>pdffs: <pre><p><del>It&#39;s common to use a single receiver type when defining methods because it&#39;s easier to work with, let&#39;s say you also have a method that modifies the receiver (in which case you want a pointer), it&#39;s more awkward to work with if you mix receivers:</del></p> <pre><code>type Page struct { title string } func (p *Page) Title(title string) { p.title = title } func (p Page) Save() { ... } </code></pre> <p><del>To call <code>Title</code> you need to obtain a pointer, or if you have a pointer, you need to deref it it call <code>Save</code>:</del></p> <pre><code>page := Page{} page.Save() // works, however... // have to obtain a pointer to call Title p := &amp;page p.Title(`blah`) // have to deref if you have a pointer, to call Save *p.Save() </code></pre> <p><del>Whereas if all methods have pointer receivers:</del></p> <pre><code>type Page struct { title string } func (p *Page) Title(title string) { p.title = title } func (p *Page) Save() { ... } </code></pre> <p><del>You just always instantiate as pointer, and can call any method:</del></p> <pre><code>page := &amp;Page{} page.Title(`blah`) page.Save() </code></pre> <p>EDIT: Apparently all wrong, apologies, see replies</p> <p>Also, to implement an interface, all methods must have the same receiver type.</p></pre>gargamelus: <pre><p>This is incorrect. You do not need to obtain a pointer or dereference a pointer to call pointer/non-pointer methods.</p> <p>Example: <a href="https://play.golang.org/p/y2uUfGhEBvj" rel="nofollow">https://play.golang.org/p/y2uUfGhEBvj</a></p></pre>mkishere: <pre><p>Except when you&#39;re implementing an interface. Modified from your example: <a href="https://play.golang.org/p/LykTghhb68R" rel="nofollow">https://play.golang.org/p/LykTghhb68R</a> <code>ChangeTitle(p)</code> will produce a compile error. The correct way of writing it should be <code>ChangeTitle(&amp;p)</code></p></pre>gargamelus: <pre><p>Yes, that is right. A non-pointer cannot satisfy an interface with pointer receivers. I just wanted to point out the incorrect info in the top reply. Thanks.</p></pre>justinisrael: <pre><p>A pointer can be a smaller copy if your struct is more than a few fields. Although your example shows a method that does not need to modify the fields of the struct, if there are other methods that do need it, then it would be a case of wanting to be consistent. Some of the methods need a pointer so just be consistent with all the methods. </p></pre>beowulf_71: <pre><p>So I am new to go, but I thought the primary reason was one of memory consumption. I dont recall now but I thought go uses small stack sizes and thus passing by value could result in a large struct being copied on the stack.. or worse, if this were a service handling 1000s of simultaneous requests (e.g. API endpoint), all that copying would quickly consume memory. I may be off on that.. dont shoot me. However, typically I would think passing a 8 byte pointer, even if you dont plan to change anything about it, would be so much faster than copying the structure to the stack for each invocation. </p> <p>For me, it is about being consistent in my code, just in case it is going to be used in ways that I am not aware of. But then, it really depends on the code too. I typically work with high availability and scalable server architectures, so things like speed and memory are very important to be aware of.</p></pre>nsd433: <pre><p>In Java all objects are accessed by reference (aka by pointer). There is no choice about it. In Go you have the choice.</p></pre>logicaleak: <pre><p>In the example that you have provided, you are right, it makes no difference. Except it is better to place the funtions you will use for a single type, in that type with a receiver. That is why your save function should belong to the struct. In that case for this example, because you dont modify anything, it is okay to have a non-pointer receiver. However, it is not the norm. Better have all receiver as the same type, which is usually a pointer because most functions somehow mutate the struct it belongs to.</p></pre>jackmott2: <pre><p>When you use Java you always are using a pointer to a Page. When you pass the pointer you are passing a reference to the same page. When you don&#39;t you are passing a copy.</p> <p>What performs better depends on what the struct is (how big it is) and how you are using it. (think about memory locality, learn about cpu caches, consider the heap and the garbage collector)</p> <p>What makes sense depends on how you are using it. If save altered the page, then you would need to use a pointer (or return a new copy of Page so you could get the change)</p> <p>The inability of Java to pick one or the other is a source of major performance problems sometimes. The JVM does a great job of mitigating that but it isn&#39;t perfect.</p></pre>wesatloldotcat: <pre><p>Lot of good answers here, but this is the official answer from the <a href="https://github.com/golang/go/wiki/CodeReviewComments#receiver-type" rel="nofollow">Go Github wiki</a>. </p> <p>tl;dr pointer shows intent to mutate, but it&#39;s also nice if you&#39;re working with big data types. </p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

460 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传