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?
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.
You can simply dereference b
: (*b)
https://play.golang.org/p/Xq6qFy4_PC
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
}
@Serdmanczyk makes a good point... you can safely pass a map around by value, the underlying map will be updated, not a copy of the map. That is to say; pass-by-value in the case of a map means passing the address of the map, not the contents of the map.
See https://play.golang.org/p/i7Yz4zMq4v
type foo map[string]string
func main() {
a := foo{}
a["hello"] = "world"
fmt.Printf("%#v\n", a)
mod(a)
fmt.Printf("%#v\n", a)
}
func mod(f foo) {
f["hello"] = "cruel world"
}
Which outputs:
main.foo{"hello":"world"}
main.foo{"hello":"cruel world"}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With