Go: invalid operation – type *map[key]value does not support indexing

gopass-by-reference

I'm trying to write a function that modifies original map that is passed by pointer but Go does not allow it. Let's say I have a big map and don't want to copy it back and forth.

The code that uses passing by value is working and is doing what I need but involves passing by value (playground):

package main

import "fmt"

type Currency string

type Amount struct {
    Currency Currency
    Value float32
}

type Balance map[Currency]float32

func (b Balance) Add(amount Amount) Balance {
    current, ok := b[amount.Currency]
    if ok {
        b[amount.Currency] = current + amount.Value
    } else {
        b[amount.Currency] = amount.Value
    }
    return b
}

func main() {
    b := Balance{Currency("USD"): 100.0}
    b = b.Add(Amount{Currency: Currency("USD"), Value: 5.0})

    fmt.Println("Balance: ", b)
}

But if I try to pass parameter as pointer like here (playground):

func (b *Balance) Add(amount Amount) *Balance {
    current, ok := b[amount.Currency]
    if ok {
        b[amount.Currency] = current + amount.Value
    } else {
        b[amount.Currency] = amount.Value
    }
    return b
}

I'm getting compilation error:

prog.go:15: invalid operation: b[amount.Currency] (type *Balance does not support indexing)
prog.go:17: invalid operation: b[amount.Currency] (type *Balance does not support indexing)
prog.go:19: invalid operation: b[amount.Currency] (type *Balance does not support indexing)

How should I deal with this?

Best Answer

You are trying to index on the pointer rather than the map itself. Kind of confusing because usually with pointers vs. values dereferencing is automatic for structs. If your struct is just a map, however, it's only passed in by reference anyway so you don't have to worry about creating methods that act on pointers to avoid copying the entire structure every time. The following code is equivalent to your first snippet but using a pointer type.

package main

import "fmt"

type Currency string

type Amount struct {
    Currency Currency
    Value float32
}

type Balance map[Currency]float32

func (b *Balance) Add(amount Amount) *Balance {
    current, ok := (*b)[amount.Currency]
    if ok {
        (*b)[amount.Currency] = current + amount.Value
    } else {
        (*b)[amount.Currency] = amount.Value
    }
    return b
}

func main() {
    b := &Balance{Currency("USD"): 100.0}
    b = b.Add(Amount{Currency: Currency("USD"), Value: 5.0})

    fmt.Println("Balance: ", (*b))
}

But to answer how to deal with it: if your struct is just of type map, I wouldn't worry about writing your receiving functions to take pointers, and just receive the value since the value is only a reference anyways. Do like in your original snippet.

Related Topic