关于go:为什么在将结构体的值设置为地图中的值时,会出现“无法分配”错误?


Why do I get a “cannot assign” error when setting value to a struct as a value in a map?

本问题已经有最佳答案,请猛点这里访问。

全新体验。遇到此错误,无法找到原因或原因:

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

1
2
3
4
5
6
7
8
9
10
11
type Person struct {
 name string
 age int
}

func main() {
  x := Person{"Andy Capp", 98}
  x.age = 99
  fmt.Printf("age: %d
", x.age)
}

但如果结构是映射中的一个值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
", p["HM"].age)
}

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

我找到了一种解决方法——创建一个incrementAgefunc-on-person,可以调用它并将结果分配给map键,例如p["HM"] = p["HM"].incrementAge()

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


p["HM"]不是一个很规则的可寻址值:哈希映射可以在运行时增长,然后在内存中移动它们的值,旧位置就过时了。如果将映射中的值视为常规的可寻址值,那么map实现的那些内部将暴露出来。

因此,p["HM"]与规范中的"map index expression"略有不同;如果您在规范中搜索短语"index expression",您将看到可以对它们执行某些操作,例如读取它们、分配它们,并在递增/递减表达式中使用它们(对于数字类型)。但你不能什么都做。他们本可以选择实施比他们更特殊的案例,但我猜他们不仅仅是为了保持简单。

您的方法在这里似乎很好——您将其更改为常规分配,这是特别允许的操作之一。另一种方法(也许对您希望避免复制的大型结构比较好?)使映射值成为常规的旧指针,您可以通过以下方式修改基础对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
", p["HM"].age)
}


任务的左侧必须为"可寻址"。

https://golang.org/ref/spec分配

Each left-hand side operand must be addressable, a map index expression, or (for = assignments only) the blank identifier.

和https://golang.org/ref/spec_address_operators

The operand must be addressable, that is, either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array.

正如@twotwootwo的评论,p["HM"]是不可寻址的。但是,在SEPC中没有这样的定义来显示什么是"可寻址结构操作数"。我想他们应该加一些描述。