2 回答

TA貢獻1883條經驗 獲得超3個贊
映射索引表達式是不可尋址的,因為當向映射添加新條目時,映射的內部可能會發生變化,因此規范故意不允許獲取其地址(這為映射實現提供了更大的自由度)。
這意味著如果您在映射中存儲非指針,并且想要調用具有指針接收器的存儲值的方法,則需要獲取非指針值的地址(用作接收器) ,但由于映射索引表達式不可尋址,因此會導致編譯時錯誤。
解決方法是將指針值存儲在映射中,因此不需要獲取索引表達式的地址,因為它已經是一個指針。在這個答案中可以看到這樣的一個例子:Why should constructor of Go return address? 如果我們有這種類型:
type My int
func (m *My) Str() string { return strconv.Itoa(int(*m)) }
這給出了相關的編譯時錯誤:
m := map[int]My{0: My(12)}
m[0].Str() // Error!
但這有效:
m := map[int]*My{}
my := My(12)
m[0] = &my // Store a pointer in the map
m[0].Str() // You can call it, no need to take the address of m[0]
// as it is already a pointer
另一種選擇是將其分配給一個可以獲取其地址的局部變量,并對其調用指針方法。但必須小心,就像該方法具有指針接收器一樣,它可能會修改指向的對象或其組件(例如結構體的字段),這不會反映在存儲在映射中的值中。如果您沿著這條路徑走下去,您可能必須將值重新分配給映射中的鍵才能獲得更新的值。
總而言之,如果您的值的類型具有帶有指針接收器的方法,那么最好將其(存儲、傳遞)用作指針而不是非指針值。

TA貢獻1848條經驗 獲得超2個贊
下面是一個示例,說明“值接收器”與“指針接收器”如何與“指針映射”與“值映射”交互:
https://play.golang.org/p/JVp6DirgPkU
package main
import (
"fmt"
)
// a simple type, with two methods : one with a value receiver, one with a pointer receiver
type Item struct {
name string
}
func (i Item) GetNameByValue() string {
return i.name
}
func (i *Item) GetNameByRef() string {
return i.name
}
func main() {
{
// in this map, we store *pointers* to Item values
mapByRef := make(map[int]*Item)
mapByRef[0] = &Item{"I am stored as a pointer"}
// GetNameByRef will work on a *Item : "mapByRef[0]" is already a pointer
fmt.Println("GetByRef :", mapByRef[0].GetNameByRef())
// GetNameByValue will work on a *Item : go automatically turns this into '(*mapByRef[0]).GetNameByValue()', and this is valid
fmt.Println("GetByValue :", mapByRef[0].GetNameByValue())
}
{
// in this map, we store Item values (no pointers)
mapByValue := make(map[int]Item)
mapByValue[0] = Item{"I am stored as a value"}
// GetNameByValue will work on a Item : "mapByValue[0]" has the right type
fmt.Println("GetByValue :", mapByValue[0].GetNameByValue())
// GetNameByRef will not work : go tries to turn this into : (&mapByValue[0]).GetNameByRef(),
// and go refuses to let you take the address of a value inside a map
// fmt.Println("GetByRef :", mapByValue[0].GetNameByRef())
// compiler error :
// ./prog.go:47:46: cannot call pointer method on mapByValue[0]
// ./prog.go:47:46: cannot take the address of mapByValue[0]
// you will need some way to copy the value before taking its address :
item := mapByValue[0]
fmt.Println("item.GetByRef :", item.GetNameByRef())
// same as :
fmt.Println("(&item).GetByRef :", (&item).GetNameByRef())
}
}
// Output :
//
// GetByRef : I am stored as a pointer
// GetByValue : I am stored as a pointer
// GetByValue : I am stored as a value
// item.GetByRef : I am stored as a value
// (&item).GetByRef : I am stored as a value
- 2 回答
- 0 關注
- 154 瀏覽
添加回答
舉報