为什么在Go语言中不能将数组分配给映射中的元素?

huangapple go评论73阅读模式
英文:

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

问题

这里有一个简短的示例来说明:

package main

import "fmt"

func main() {
	array := [3]int{1, 2, 3}
	array[0]++ // 可行
	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]++ // 无法编译通过:"cannot assign to arrayMap[0][0]"
	sliceMap[0][0]++
	fmt.Println(arrayMap)
	fmt.Println(sliceMap)
}

为什么如果数组在映射中,我不能修改其内容,即使在映射外部它们是可变的?而对切片来说为什么可以?

英文:

Here's a short example to demonstrate:

package main

import "fmt"

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: "cannot assign to arrayMap[0][0]"
	sliceMap[0][0]++
	fmt.Println(arrayMap)
	fmt.Println(sliceMap)
}

Why can I not modify the contents of an array if it's inside a map, even though they are mutable outside the map? And why does this work with slices?

答案1

得分: 6

对于映射(maps),它的值是不可寻址的。也就是说,当你使用一个“值类型”(在Go中,数组是“值类型”)时,你不能使用“++”来寻址该值。

但是,如果你使用一个“引用类型”(在Go中,切片是“引用类型”),就可以像你在示例中已经提到的那样。

无论在映射中使用的类型是什么,这个规则都是适用的。

我们可以做的一件事是使用类型的指针地址。例如,如果你取数组的地址,那么它应该可以工作:

Playground: http://play.golang.org/p/XeIThVewWD

package main

import "fmt"

func main() {
    array := [3]int{1, 2, 3}
    slice := []int{1, 2, 3}
    
    arrayMap := make(map[int]*[3]int) // 使用类型的指针
    sliceMap := make(map[int][]int)
    arrayMap[0] = &array // 获取类型的指针
    sliceMap[0] = slice
    
    arrayMap[0][0]++ // 可以工作,因为它是数组的指针
    sliceMap[0][0]++
    
    fmt.Println(*arrayMap[0])
    fmt.Println(sliceMap[0])
}

// 输出
[2 2 3]
[2 2 3]

这个代码可以工作,并将array[0]索引递增为2,与预期相符。

这是因为Go在读取指针时,会优雅地对其进行解引用,并在重新赋值时更新该值。

英文:

For maps, its values are not addressable. That is, when you use a value type (arrays are value types in Go), you cannot address the value using ++.

But, if you use a reference type (slices are reference types in Go), you can as you already alluded to in the example.

This holds true regardless of type used in the Map.

One thing we can do instead is to use the ptr address of the type. For example, if you take the address of the array, then it should work:

Playground: http://play.golang.org/p/XeIThVewWD

package main

import "fmt"

func main() {
	array := [3]int{1, 2, 3}
	slice := []int{1, 2, 3}
	
	arrayMap := make(map[int]*[3]int) // use the pointer to the type
	sliceMap := make(map[int][]int)
	arrayMap[0] = &array // get the pointer to the type
	sliceMap[0] = slice
	
	arrayMap[0][0]++ // works, because it's a pointer to the array
	sliceMap[0][0]++
	
	fmt.Println(*arrayMap[0])
	fmt.Println(sliceMap[0])
}

// outputs
[2 2 3]
[2 2 3]

This works and increments the [0] index of array to 2, as expected.

It works because Go graciously dereferences pointers for us to its value when read and updates the value during re-assignment.

huangapple
  • 本文由 发表于 2016年4月2日 11:05:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/36368704.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定