Use as a pointer or a value?

polaris · · 475 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Most types I see in Go are used either as a pointer or a value. <code>bytes.Buffer</code>, for example, is used as a pointer. I can imagine why. <code>bytes.Buffer</code> implements <code>Reader</code>, <code>Writer</code> and other IO interfaces on it&#39;s pointer type. </p> <p>I also looked into other packages. <code>ast.ExprStmt</code> is also used as a pointer. It&#39;s methods do not modify its value, but it still has pointer receivers when implementing <code>ast.Node</code>, and is used as (passed around) as a pointer. (idk why)</p> <p>So my question is when should you use a type as a pointer or a value? </p> <p>If I have a type like this: </p> <pre><code>type NamedMap struct { Name string Map map[something]something } </code></pre> <p>should I pass it around as a pointer or a value?</p> <hr/>**评论:**<br/><br/>SilentWeaponQuietWar: <pre><p>To crudely paraphrase a passionate Go instructor I saw at a recent GopherCon... values / reference should be determined by readability and consistency.</p> <p>Look at the factory pattern - does it return a pointer, or a value? If it returns a pointer, ALWAYS use a pointer for that type of value. If it returns a value, ALWAYS use a value, and never convert into a pointer.</p> <p>This makes it easy to keep a more accurate mental model. If you are converting things between values and pointers and back again in various places, it will be impossible to maintain an accurate mental model of your code.</p> <p>As for optimization and performance considerations, they should all be secondary to readability.</p></pre>tv64738: <pre><p><code>ast.ExprStmt</code> probably gets stored into <code>ast.Node</code> interface slots all the time, at which point it would need an allocation &amp; be stored as pointer anyway.</p></pre>winger_sendon: <pre><p>That&#39;s implementation specific though. </p></pre>tv64738: <pre><p>Sure, but it&#39;s core code so it&#39;s not like the authors didn&#39;t understand the implications, or were unsure of them.</p> <p>The other good justification is &#34;if some of ast.Nodes are *T, then for consistency let&#39;s make all be pointers&#34;. That makes the type switches easier to write.</p></pre>epiris: <pre><p>To expand on his post, It&#39;s not as one dimensional of a decision as mutability. Structures that are read often but larger in size (ast) may make sense to be pointers as well, if every single implementation of Node had a Value receiver you would spend all your time copying around memory just to read it. That doesn&#39;t make sense of course, you wouldn&#39;t:</p> <pre><code>cat file-I-want-to-read.txt &gt; copy.txt more copy.txt rm copy.txt </code></pre></pre>snazzyjackets: <pre><p>To avoid the possibly largish copy. Pointers are just a word(a few bytes) while the types pointed to could be many bytes. The indirection is possibly cheaper than the copy, so a pointer was used. Imagine a program moving that type through many function calls, that copying begins to add up. Now, imagine that type just being a pointer, the load has been significantly reduced.</p> <p>Your type stores 3 words. A pointer is 1 word. There is potential for both to be beneficial. Indirection vs copying. Pick one and profile.</p></pre>deusmetallum: <pre><p>Every time you pass a variable to a function, you are making a copy of it. That means that any change you make to it inside the function is only being made to the copy and not the original.</p> <p>A pointer is simply a reference to the memory location of the original value, so when you pass that to a function, you&#39;re making a copy of the memory location (rather than the value), and when you make changes to the pointer, you&#39;re making changes to the original thing.</p> <p>A secondary reason for using pointers, especially for buffers, etc, is that the memory address of a variable is very small, but the buffer could be very big. If you copied the whole buffer every time you needed to change it, you would end up using all your memory in no time.</p></pre>winger_sendon: <pre><p>I know why we use pointers instead of values when passing them to functions and as receiver.<br/> But buffer aside, why is ast.ExprStmt passed around as a pointer? Non of it&#39;s methods change its value, but they still have pointer receivers. It&#39;s also not that big, only stores an interface.</p></pre>tmornini: <pre><p>Perhaps for consistency with other code, perhaps for performance: if there is no asynchronous access it&#39;s less work to pass a pointer than to pass the presumably larger struct.</p></pre>aerook: <pre><p>Regarding receivers on function definitions: If a variable is in scope as a concrete type, any function that is defined on the type can be invoked. If the variable is a value and a pointer method is called, Go converts the variable to a pointer under the hood, so the method can be invoked. Value methods can be called on pointer variables without issue.</p> <p>The pointer/value receivers really make a difference when attempting to satisfy an interface. If a variable is in scope as an interface, Go cannot do the pointer conversion.</p> <p>This playground shows that pointer and value functions can be called on both pointers and values, and that when satisfying an interface, the interface must be satisfied by whatever is being used (value or pointer). If you uncomment line #31, you can see that the value object does not satisfy the interface, as the function is declared on the pointer: <a href="https://play.golang.org/p/PCpsTQFmvS" rel="nofollow">https://play.golang.org/p/PCpsTQFmvS</a></p></pre>winger_sendon: <pre><p>Then <code>ast.ExprStmt</code> could easily implement <code>ast.Node</code> using only value receivers because its methods do not modify its value. But it&#39;s still used as a pointer. Could it be just to be consistent with other nodes?<br/> In .NET for example, we use classes (reference type - go pointers) almost all the time. And use structs if the value is small, behaves like a value, has no identity</p></pre>Veonik: <pre><p>It is likely for consistency, yes.</p> <p>Remember, there are no reference/value types in Go. Everything is a value in Go, including pointers. A pointer is still copied when passed as an argument (a receiver is just another argument) but the underlying memory it points to is not.</p> <p>Additionally, the programmer doesn&#39;t have much say in whether a value is placed on the stack or the heap. The Go runtime decides, so a non-pointer may still end up on the heap. This is different from .NET where a class is always allocated on the heap, and a bare struct is usually allocated on the stack.</p></pre>winger_sendon: <pre><p>Thanks. So what do you say about my <code>NamedMap</code> type? It doesnt have have any methods. Should I use it (for example as a field in another struct, or return from a function) as a pointer or value?</p></pre>Veonik: <pre><p>It depends on how you use the <code>NamedMap</code>. If you have other functions that receive the map and modify it, then a pointer should be used.</p> <p>However, the <code>NamedMap</code> contains a real <code>map</code>, which is a pointer-like value. So even if something receives a bare <code>NamedMap</code>, modifying the map stored in the <code>Map</code> field will modify the underlying map that all copies of that specific <code>NamedMap</code> point to. <a href="https://play.golang.org/p/eFBtckFvO-" rel="nofollow">Here is a salient example</a></p> <p>One final point, you mention that <code>NamedMap</code>has no methods, but methods are simply syntactic sugar in Go. The receiver argument is just a regular argument under the hood, with some additional semantics that &#34;bind&#34; the value of the argument to whatever struct its attached to.</p> <p>edit: I realize I rambled a lot but didn&#39;t actually answer your question. To me, there is no hard-and-fast rule for returning a value instead of a pointer. Often-times I will return values for wrappers around things that I know shouldnt change, or that I want to enforce are immutable. The NamedMap struct in your example: if the Name field should never change after initialization, then return the value. But if Name might be really long, it might make more sense to return a pointer, avoiding copying the string every time you pass around the NamedMap.</p></pre>winger_sendon: <pre><p>Thank you. I kind of understand it now.</p></pre>

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

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