[Beginner Question] When does golang make a copy of value?

polaris · · 471 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Is there a list of situation where golang make a copy of value?</p> <p>I know that if the function parameter is by value, and brand new copy will be created.</p> <pre><code>func pass_by_value(value interface{}) </code></pre> <p>is there any other situation that golang will create a new copy?</p> <hr/>**评论:**<br/><br/>MattieShoes: <pre><p>Strictly speaking, all parameters are passed by value. When you&#39;re passing a reference (e.g. a slice), it makes a copy of that reference.</p></pre>natefinch: <pre><p>please don&#39;t use the word reference. There are no references in go. There are pointers, which are just a value that you can use to access a location in memory. I know it seems silly, but the word reference confuses people. Go has pointers, that is all.</p></pre>MattieShoes: <pre><p>Really? golang.org uses the word reference.</p> <p>Besides, what do you call following a pointer?</p></pre>elagergren: <pre><p>Does it? Rob Pike has even expressed remorse that he ever used &#39;reference&#39; when referring to slices, maps, channels, etc. because they have concrete types and semantics that differ from true references.</p> <blockquote> <p>Besides, what do you call following a pointer?</p> </blockquote> <p>I don&#39;t know how this makes your point. You de-reference a <em>pointer</em> not a reference. In other languages with true references you can&#39;t de-ref a reference—it just <em>is</em>.</p></pre>MattieShoes: <pre><p>It does. From the FAQ: </p> <blockquote> <p>Slices and maps act as references</p> </blockquote> <p>I&#39;m not going to get into the definition of reference debate. Go absolutely has references simply because it can have things that refer to something else. That&#39;s what a reference is. Some CS people want it to mean something else, something more specific. That&#39;s fine for them -- for them, Go may not have references. For myself -- I&#39;m not going to break a common English word.</p></pre>metamatic: <pre><p>Indeed. The primary difference in meaning is that a pointer must be explicitly dereferenced; whereas a reference, once created, is implicitly dereferenced whenever its symbol is used.</p> <p>In that sense, slices are references (plus some data size information), not pointers, because the internal pointer in the slice data structure is dereferenced automatically and invisibly when the slice is used.</p></pre>elagergren: <pre><blockquote> <p>For myself -- I&#39;m not going to break a common English word.</p> </blockquote> <p>I think most of the pushback comes from a situation like this:</p> <pre><code>var a []int func foo(a []int) { a = append(a, 1, 2, 3) } func main() { foo(a) } </code></pre> <p>Obviously it&#39;s a dumb little example, but it&#39;s not unusual to see newcomers to Go assume slices, maps, etc. function identically to &#34;true&#34; references (a la C++) because people call them references.</p></pre>MattieShoes: <pre><p>EDIT: never mind, not getting into this argument :-)</p></pre>elagergren: <pre><p>:-)</p></pre>borderline1p: <pre><p>I think <code>a := b</code> is not pass by value but by reference?</p></pre>hanmunjae: <pre><p>There&#39;s no &#34;passing&#34; at all there, since you&#39;re not calling a function. <code>a := b</code> does make a copy of <code>b</code>, but if <code>b</code> is an interface, pointer, or reference type (<code>map</code>, <code>chan</code>, slice), then (1) the copy is cheap, and (2) modifying one of <code>a</code> or <code>b</code> will modify the other.</p></pre>ChristophBerger: <pre><blockquote> <p>if b is an interface, pointer, or reference type (map, chan, slice), then (1) the copy is cheap</p> </blockquote> <p>...because only the pointer itself is copied, or the &#34;header&#34; struct in case of a map, chan or slice. This is a typical pitfall if you are used to languages that do a deep copy of all parameters when calling a function.</p> <blockquote> <p>and (2) modifying one of a or b will modify the other.</p> </blockquote> <p>Almost, but there is a subtle distinction to consider. After passing a map, slice, channel, or pointer to a function, modifying the <em>contents</em> of a map, slice, or channel, as well as modifying the data that a copied pointer <em>points to</em>, also modifies the original data (because no deep copy happens when passing parameters to a function). </p> <p>However, if the called function modifies anything in the header struct itself, like for example, assigning a new slice to a copied slice variable, changing the length or capacity of a slice, or if it assigns a new value to a pointer, then this does not affect the values of the caller. These changes are lost when the function returns.</p> <p>(This is the reason why <code>append()</code> <em>returns</em> the slice that it modified, otherwise the original slice would not be able to receive any change made to the header (that is, the length, capacity, or the actual slice data pointed to).)</p></pre>borderline1p: <pre><p>woah, so <code>a := b</code> will create a new copy of <code>b</code>?</p> <p>like say that you have</p> <pre><code>type struct_one struct { nested1 struct_two } type struct_two struct { nested2 struct_three } type struct_three struct { some_int int } </code></pre> <p>if </p> <pre><code>nested_level2 = struct_three{} nested_level1 = struct_two{nested_level2} nested_level0 = struct_one{nested_level1} a := nested_level0 </code></pre> <p>Does this mean </p> <p><code>a</code> will have a different address than <code>nested_level0</code></p> <p><code>a.nested1</code> will have a different address than <code>nested_level0.nested1</code></p> <p><code>a.nested1.nested2</code> will have a different address than <code>nested_level0.nested1.nested2</code> ?</p></pre>sharno: <pre><p>Yep:</p> <p><a href="https://play.golang.org/p/VygP7SR9or" rel="nofollow">https://play.golang.org/p/VygP7SR9or</a></p></pre>sharno: <pre><p>I had to add an int for every struct as when I didn&#39;t, all the nesting is useless. All nested structs have the same address in memory like the parent one as no need for more space in memory to add the nested struct. (you can remove the ints inside the structs from the link I posted and you&#39;ll see what I mean)</p></pre>borderline1p: <pre><p>I see thanks for the playground, I didn&#39;t know that golang does this by default.</p></pre>natefinch: <pre><p>Go is always pass by value. Always. Sometimes that value is a pointer, but it&#39;s still a copy.</p> <p>maps, functions, channels, slices, and interfaces are all pointers underneath the hood. (and obviously anything that is explicitly a *foo type)</p></pre>sharno: <pre><p>All is pass by value unless you pass a pointer. But passing by value for a <strong>map</strong> or <strong>slice</strong> means copying the reference. So passing map or slice by value is same as passing their pointers, when you modify them inside a function the original ones are modified as well. While passing <strong>int</strong>, <strong>string</strong>, <strong>array</strong>, <strong>struct</strong> copies all content. So modifying them inside a function will not modify the original (unless you pass a pointer)</p> <p>interesting cases:</p> <pre><code>func main() { a := []int{1,2,3} change(a) fmt.Println(a) } func change(a []int) { a[0] = 4 // this will modify original a = append(a, 5) // this won&#39;t modify the original } </code></pre> <p>will print [4 2 3]</p> <pre><code>func main() { a := []int{1,2,3} change(&amp;a) fmt.Println(a) } func change(a *[]int) { (*a)[0] = 4 // this will modify original *a = append(*a, 5) // this will modify the original as well } </code></pre> <p>will print: [4 2 3 5]</p> <p>I think passing the map as a pointer or value doesn&#39;t make a difference. Can someone confirm this?</p></pre>sharno: <pre><p>I think that&#39;s the same reason that the zero value of a map or slice is nil while all other types are usable directly even without initialization (their zero values are not nil)</p></pre>sharno: <pre><p>Also reading those two posts gives a much better idea:</p> <p><a href="https://blog.golang.org/go-slices-usage-and-internals" rel="nofollow">https://blog.golang.org/go-slices-usage-and-internals</a></p> <p><a href="https://blog.golang.org/go-maps-in-action" rel="nofollow">https://blog.golang.org/go-maps-in-action</a></p></pre>borderline1p: <pre><p>Thanks for linking this, I am reading them right now, and they are super helpful.</p></pre>

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

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