英文:
Calling a pointer method on a struct in a map
问题
有一个类型为Company的结构体,其中包含一个Person的映射,这些Person也都是结构体。
type Company struct {
employees map[int]Person
}
type Person struct {
[...]
}
在将一些Person分配给employees映射之后,我尝试在每个Person上调用一个指针方法。
func (company *Company) Populate(names []string) {
for i := 1; i <= 15; i++ {
company.employees[i] = Person{names[i - 1], [...]}
company.employees[i].Initialize()
}
}
这个方法失败了,go编译器抱怨我不能在*company.employees[i]上调用指针方法,也不能取得company.employees[i]*的地址。
然而,将Initialize方法设置为非指针方法,并让它返回person的副本,然后通过使用
company.employees[i] = company.employees[i].Initialize()
再次将其分配给映射,这样就可以工作,这并没有太大的区别。
我从未使用过指针,这让我很困扰。映射是可变的,并且无论如何都会被修改,所以在映射中调用实体的指针方法不应该是个问题 - 至少在我看来是这样的。
如果有人能解释一下我在这里做错了什么 - 或者纠正我的思维 - 我会很高兴。
英文:
There is the type Company, which is a struct, containing a map of Person's, which are also all structs.
type Company struct {
employees map[int]Person
}
type Person struct {
[...]
}
After allocating some Person's to the employees-map, I'm trying to call a pointer method on each of these.
func (company *Company) Populate(names []string) {
for i := 1; i <= 15; i++ {
company.employees[i] = Person{names[i - 1], [...]}
company.employees[i].Initialize()
}
}
This miserably fails, with the go-Compiler complaining that I can't call pointer methods on company.employees[i], as well as I can't take the address of company.employees[i].
However, setting the Initialize-method to a non-pointer method and letting it return the copy of the person, and allocating it to the map again by using
company.employees[i] = company.employees[i].Initialize()
works, which is not that different.
Not having worked with pointers ever this bugs me quite much. Map's aren't immutable, and they get modified either way, so calling a pointer method on an entity in a map shouldn't be a problem - atleast in my head.
If anyone could explain to me what I'm doing wrong here - or correct my thinking - I'd be pleased.
答案1
得分: 16
这里的问题是,为了调用指针方法,需要获取employees[i]
的地址。根据Go规范:
操作数必须是可寻址的,即变量、指针间接引用或切片索引操作;或者是可寻址结构操作数的字段选择器;或者是可寻址数组的数组索引操作。作为对可寻址要求的例外,x也可以是复合字面量。
映射索引操作是不可寻址的。这样决定是为了使映射实现不需要保证值的地址不会改变。随着更多数据添加到映射中,为了提高效率,它可能会重新定位数据。
那么,你该如何修复它呢?如果你有一个map[int]*Person
,你就不需要获取映射中数据的地址,因为映射值本身就是一个地址。
最后一点建议,Person.Initialize()
不是非常符合Go的惯用代码。如果你需要初始化一个类型,通常会使用NewPerson()
函数。NewPerson()
函数会返回一个初始化的Person结构体或指针。
英文:
The problem here is that in order to call a pointer method, the address of employees[i]
needs to be taken. According to the Go specification:
> 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. As an exception to the addressability requirement, x may also be a composite literal.
A map indexing operation is not addressable. This was decided so that map implementations would not need to guarantee that the addresses of values would not change. As more data is added to the map, it may relocate data for efficiency reasons.
So, how can you fix it? If you have a map[int]*Person
, you will not need to take the address of the data in the map since the map value is already an address.
One last bit of advice, Person.Initialize()
is not very idiomatic Go code. If you need to initialize a type, you normally use a NewPerson()
function. The NewPerson()
function would return an initialized Person struct or pointer.
答案2
得分: 0
例如,
package main
import "fmt"
type Person struct {
Name string
}
func NewPerson(name string) *Person {
return &Person{Name: name}
}
type Company struct {
Employees map[int]*Person
}
func (c *Company) Populate(names []string) {
c.Employees = make(map[int]*Person)
for i := range names {
c.Employees[i+1] = NewPerson(names[i])
}
}
func main() {
c := Company{}
c.Populate([]string{"Peter", "Paul"})
for k, v := range c.Employees {
fmt.Println(k, *v)
}
}
输出:
1 {Peter}
2 {Paul}
英文:
For example,
package main
import "fmt"
type Person struct {
Name string
}
func NewPerson(name string) *Person {
return &Person{Name: name}
}
type Company struct {
Employees map[int]*Person
}
func (c *Company) Populate(names []string) {
c.Employees = make(map[int]*Person)
for i := range names {
c.Employees[i+1] = NewPerson(names[i])
}
}
func main() {
c := Company{}
c.Populate([]string{"Peter", "Paul"})
for k, v := range c.Employees {
fmt.Println(k, *v)
}
}
Output:
1 {Peter}
2 {Paul}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论