Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a pointer method on a struct in a map

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.

like image 219
Big-Blue Avatar asked Oct 27 '12 15:10

Big-Blue


1 Answers

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.

like image 99
Stephen Weinberg Avatar answered Sep 25 '22 01:09

Stephen Weinberg