为结构中的值设置为地图中的值时,为什么会出现"无法分配"错误?

sbe*_*eam 55 go

Go to Go.遇到这个错误,没有找到原因或理由:

如果我创建一个结构,我显然可以分配和重新分配值没有问题:

type Person struct {
 name string
 age int
}

func main() {
  x := Person{"Andy Capp", 98}
  x.age = 99
  fmt.Printf("age: %d\n", x.age)
}
Run Code Online (Sandbox Code Playgroud)

但如果结构是地图中的一个值:

type Person struct {
     name string
     age int
 }

type People map[string]Person

func main() {
  p := make(People)
  p["HM"] = Person{"Hank McNamara", 39}
  p["HM"].age = p["HM"].age + 1
  fmt.Printf("age: %d\n", p["HM"].age)
}
Run Code Online (Sandbox Code Playgroud)

我得到cannot assign to p["HM"].age.就是这样,没有其他信息.http://play.golang.org/p/VRlSItd4eP

我找到了解决这个问题的方法 - incrementAge在Person上创建一个func,可以调用它,并将结果分配给map键,例如p["HM"] = p["HM"].incrementAge().

但是,我的问题是,这个"无法分配"错误的原因是什么,为什么不允许我直接分配结构值?

two*_*two 88

p["HM"]不是一个常规的可寻址值:hashmaps可以在运行时增长,然后它们的值在内存中移动,旧位置变得过时.如果映射中的值被视为常规可寻址值,那么map实现的内部结构将会暴露出来.

所以,相反,在规范中p["HM"]称为"映射索引表达式 "的东西略有不同; 如果搜索短语"索引表达式"的规范,你会看到你可以用它们做某些事情,比如读它们,分配它们,并在递增/递减表达式中使用它们(对于数字类型).但你无法做到一切.他们本可以选择实施比他们更多的特殊情况,但我猜他们不仅仅是为了让事情变得简单.

你的方法在这里似乎很好 - 你将它改为常规任务,这是一项特别允许的操作.另一种方法(对于你想避免复制的大型结构可能有用吗?)是使map值成为一个常规的旧指针,你可以通过以下方式修改底层对象:

package main

import "fmt"
type Person struct {
     name string
     age int
 }

type People map[string]*Person

func main() {
  p := make(People)
  p["HM"] = &Person{"Hank McNamara", 39}
  p["HM"].age += 1
  fmt.Printf("age: %d\n", p["HM"].age)
}
Run Code Online (Sandbox Code Playgroud)

  • 使用指针映射的建议,例如`map[string]*MyStruct` 为我解决了类似的问题。 (12认同)

Jia*_* YD 5

分配的左侧必须是“可寻址的”。

https://golang.org/ref/spec#Assignments

每个左侧操作数必须是可寻址的、映射索引表达式或(仅用于 = 赋值)空白标识符。

https://golang.org/ref/spec#Address_operators

操作数必须是可寻址的,即变量、指针间接或切片索引操作;或可寻址结构操作数的字段选择器;或可寻址数组的数组索引操作。

正如@twotwotwo 的评论,p["HM"]是不可寻址的。但是,没有这样的定义显示规范中的“可寻址结构操作数”是什么。我认为他们应该为它添加一些描述。