为什么我得到一个"不能分配"将值设置为结构中的值作为地图中的值时出错?

时间:2015-09-24 00:29:53

标签: go

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)
}

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

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)
}

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

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

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

2 个答案:

答案 0 :(得分:77)

p["HM"]不是常规addressable值:hashmaps can grow at runtime,然后他们的值会在内存中移动,旧位置会过时。如果映射中的值被视为常规可寻址值,则map实现的内部结构将被暴露。

所以,相反,p["HM"]是一个稍微不同的东西,称为"地图index expression"在规范中;如果您在规范中搜索短语"索引表达式"你会发现你可以用它们做某些事情,比如读它们,分配给它们,并在递增/递减表达式中使用它们(对于数字类型)。但你不能做任何事情。他们本可以选择实施更多特殊情况,但我猜他们并不只是为了让事情变得简单。

您的方法在这里似乎很好 - 您将其更改为常规分配,这是一项特别允许的操作。另一种方法(对于你想避免复制的大型结构可能有用吗?)是make the map value a regular old pointer你可以通过以下方式修改底层对象:

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)
}

答案 1 :(得分:2)

作业的左侧必须是“可寻址的”。

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

  

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

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

  

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

作为@ twotwotwo的评论,p["HM"]无法解决。 但是,没有这样的定义表明sepc中的“可寻址结构操作数”是什么。我认为他们应该为它添加一些描述。