英文:
Go: invalid operation - type *map[key]value does not support indexing
问题
我正在尝试编写一个函数,该函数修改通过指针传递的原始地图,但Go不允许这样做。假设我有一个大地图,不想来回复制它。
使用按值传递的代码可以正常工作并且实现了我需要的功能(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)
}
但是,如果我尝试像这样将参数作为指针传递(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
}
我会得到编译错误:
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)
我该如何处理这个问题?
英文:
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?
答案1
得分: 69
你正在尝试在指针上进行索引,而不是在地图本身上进行索引。这有点令人困惑,因为通常在结构体中,指针与值的解引用是自动进行的。然而,如果你的结构体只是一个地图,它只是通过引用传递,所以你不必担心创建针对指针的方法,以避免每次复制整个结构体。以下代码与你的第一个片段等效,但使用了指针类型。
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))
}
但是,要回答如何处理它:如果你的结构体只是地图类型,我不会担心编写接收函数以接受指针,而是接收值,因为该值只是一个引用。就像你原来的片段一样。
英文:
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.
答案2
得分: 26
你可以简单地解引用 b
:(*b)
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 提出了一个很好的观点...你可以安全地通过值传递一个 map,底层的 map 会被更新,而不是 map 的副本。也就是说,在 map 的情况下,按值传递意味着传递的是 map 的地址,而不是 map 的内容。
参考 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"
}
输出结果为:
main.foo{"hello":"world"}
main.foo{"hello":"cruel world"}
英文:
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
}
Update
@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"}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论