Why can one not assign to arrays inside maps in Go?

blov · · 489 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Here&#39;s a short example to demonstrate:</p> <pre><code>package main import &#34;fmt&#34; func main() { array := [3]int{1, 2, 3} array[0]++ // Works slice := make([]int, 3) for i := range slice { slice[i] = i + 1 } arrayMap := make(map[int][3]int) sliceMap := make(map[int][]int) arrayMap[0] = array sliceMap[0] = slice //arrayMap[0][0]++ // Does not compile: &#34;cannot assign to arrayMap[0][0]&#34; sliceMap[0][0]++ fmt.Println(arrayMap) fmt.Println(sliceMap) } </code></pre> <p>Why can I not modify the contents of an array if it&#39;s inside a map, even though they are mutable outside the map? And why does this work with slices?</p> <hr/>**评论:**<br/><br/>brunoB: <pre><p>From the <a href="http://blog.golang.org/go-slices-usage-and-internals" rel="nofollow">Go Slices Blogpost</a>:</p> <blockquote> <p>Go&#39;s arrays are values. An array variable denotes the entire array; it is not a pointer to the first array element (as would be the case in C). This means that when you assign or pass around an array value you will make a copy of its contents. (To avoid the copy you could pass a pointer to the array, but then that&#39;s a pointer to an array, not an array.) One way to think about arrays is as a sort of struct but with indexed rather than named fields: a fixed-size composite value.</p> </blockquote> <p>Slices are pass by reference, while arrays are pass by value.</p> <p>This means that the array is actually copied when put inside the map and when you get it out of the map it is copied again. I would imagine that is why the slice is immutable when in the map.</p> <p>Check this playground. <a href="http://play.golang.org/p/UqhxWXE_Xo" rel="nofollow">http://play.golang.org/p/UqhxWXE_Xo</a></p> <p>You&#39;ll notice that modifying the result of getting an array from the map actually doesn&#39;t modify the original array, while modifying the slc variable modifies the original slice.</p></pre>thockin: <pre><p>Why then, given a map of with an int value can I say m[i]++, and that sticks but m[i][j]++ doesn&#39;t?</p></pre>brunoB: <pre><p>I&#39;m not sure I understand your question. But I will try to answer what I think you&#39;re asking.</p> <pre><code>m := map[int]int{1: 1} m[1]++ // this works </code></pre> <p>This can be expanded to:</p> <pre><code>m[1] = m[1] + 1 </code></pre> <p>Which makes sense. We are assigning an integer value back into this map.</p> <pre><code>m := map[int][1]int{1: [1]int{1}} m[1][0]++ // This doesn&#39;t work </code></pre> <p>When you expand this last line, you get:</p> <pre><code>arr := m[1] arr[0] = arr[0] + 1 </code></pre> <p>Because <code>arr</code> is actually a copy of what was in the <code>m[1]</code> slot, modifying the result doesn&#39;t actually change what is in the map.</p></pre>mcouturier: <pre><p>int[3] is a type in itself, different from int[4], and those types as he said are passed by value in go.</p></pre>thockin: <pre><p>To belabor the point: if </p> <pre><code>m := map[int]int{1: 1} m[1]++ </code></pre> <p>becomes:</p> <pre><code>m[1] = m[1] + 1 </code></pre> <p>or more explicitly:</p> <pre><code>tmp := m[1] tmp++ m[1] = tmp </code></pre> <p>then:</p> <pre><code>m := map[int][1]int{1: [1]int{1}} m[1][0]++ </code></pre> <p>should become:</p> <pre><code>tmp := m[1] tmp[0]++ m[1] = tmp </code></pre> <p>Which is valid code. So why doesn&#39;t it? Indexing into maps with integer values has some reference semantic to it - the integer can be mutated in place. This is not true for arrays and structs.</p> <pre><code>m := map[int]struct{ v int }{1: s{1}} m[1].v++ </code></pre> <p>also fails. I&#39;m not really complaining about it, except that Go is apparently either too clever for its own good or annoyingly inconsistent.</p> <p>For a <code>map[int]int</code>, what is the result type of the expression <code>m[1]</code>? How about the same expression for <code>map[int][1]int</code> ?</p></pre>dragonfax: <pre><p>You&#39;ll need to copy the array out of the map, modify the array, and then set it again as a value in the array.</p> <p>The whole array is being stored as a value in the map, and for sanity reasons golang isn&#39;t going to let you index into that opaque value directly, or take a reference to an element within it.</p> <p>Everytime you do arrayMap[0] your getting a whole new copy of the array, freshly made. Even when you do arrayMap[0][0], your making a whole copy of the entire array, then indexing into that new copy.</p> <p>You could use a pointer to the array instead. This is another reason to stick with using slices. A slice is small and contains a pointer to the array already.</p></pre>clbanning: <pre><p>or: <a href="https://play.golang.org/p/FhCF75eUtW" rel="nofollow">https://play.golang.org/p/FhCF75eUtW</a></p></pre>RalphCorderoy: <pre><p>A reference to the relevant part of <a href="https://golang.org/ref/spec" rel="nofollow">https://golang.org/ref/spec</a> that states this restriction would be handy.</p></pre>oleksii1: <pre><p>As was mentioned previously you can modify array, but you will need to use pointer to array. Any time when you assign it to another array or pass it as parameter to a function you will make a copy of array. So here is what can you do:</p> <pre><code>package main import &#34;fmt&#34; func main() { array := [3]int{1, 2, 3} // array[0]++ // Works slice := make([]int, 3) for i := range slice { slice[i] = i + 1 } arrayMap := make(map[int]*[3]int) sliceMap := make(map[int][]int) arrayMap[0] = &amp;array sliceMap[0] = slice (*arrayMap[0])[0]++ sliceMap[0][0]++ fmt.Println(array, slice) // [2 2 3] [2 2 3] } </code></pre></pre>

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

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