Go:无效操作 - 类型*map [key]值不支持索引

Ale*_*nok 20 pass-by-reference go

我正在尝试编写一个函数来修改由指针传递的原始地图,但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)
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我尝试将参数传递为像这里的指针(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
}
Run Code Online (Sandbox Code Playgroud)

我收到编译错误:

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)
Run Code Online (Sandbox Code Playgroud)

我应该怎么处理这个?

小智 33

您试图索引指针而不是地图本身.有点令人困惑,因为通常使用指针与值取消引用是结构自动的.但是,如果你的结构只是一个地图,它只是通过引用传递,所以你不必担心创建作用于指针的方法,以避免每次都复制整个结构.以下代码相当于您的第一个代码段,但使用的是指针类型.

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))
}
Run Code Online (Sandbox Code Playgroud)

但是要回答如何处理它:如果你的结构只是类型映射,我不会担心编写你的接收函数来获取指针,只是接收值,因为该值只是一个参考.在原始代码段中做.


Joh*_*don 11

您可以简单地取消引用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
}
Run Code Online (Sandbox Code Playgroud)

更新

@Serdmanczyk提出了一个很好的观点...你可以安全地按值传递地图,底层地图将被更新,而不是地图的副本.也就是说; 在地图的情况下传递值意味着传递地图的地址,而不是地图的内容.

请参阅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"
}
Run Code Online (Sandbox Code Playgroud)

哪个输出:

main.foo{"hello":"world"}
main.foo{"hello":"cruel world"}
Run Code Online (Sandbox Code Playgroud)