你的觀點與「一片不過是一個指針」沒有什麼不同。切片(和地圖)使用指針來使它們輕量化,是的,但還有更多的事情可以使它們工作。例如,切片包含有關其長度和容量的信息。
至於爲什麼會出現這種情況,從代碼的角度來看,最後一行json.Unmarshal
調用d.unmarshal()
,它執行lines 176-179 of decode.go中的代碼。它基本上說「如果該值不是指針,或者是nil
,則返回InvalidUnmarshalError
」。
該文檔或許可以爲大約事情更清晰,但考慮幾件事情:
- 將如何JSON
null
值被分配到地圖爲nil
,如果你不將指針傳遞到地圖?如果您需要修改地圖本身(而不是地圖中的項目)的能力,那麼將指針傳遞給需要修改的項目是有意義的。在這種情況下,這是地圖。
- 或者,假設您通過
nil
地圖到json.Unmarshal
。在代碼json.Unmarshal
使用最終調用等效make(map[string]string)
後,值將根據需要解組。然而,你的函數中仍然有一個nil
地圖,因爲你的地圖沒有指向任何東西。除了傳遞指向地圖的指針之外,沒有辦法解決這個問題。
但是,假設沒有必要傳遞地圖的地址,因爲「它已經是一個指針」,並且您已經初始化地圖,所以它不是nil
。然後會發生什麼?嗯,如果我繞過我通過改變線路176讀取if rv.Kind() != reflect.Map && rv.Kind() != reflect.Ptr || rv.IsNil() {
前面掛線測試,則可能發生這種情況:
`{"foo":"bar"}`: false map[foo:bar]
`{}`: false map[]
`null`: panic: reflect: reflect.Value.Set using unaddressable value [recovered]
panic: interface conversion: string is not error: missing method Error
goroutine 1 [running]:
json.(*decodeState).unmarshal.func1(0xc420039e70)
/home/kit/jstest/src/json/decode.go:172 +0x99
panic(0x4b0a00, 0xc42000e410)
/usr/lib/go/src/runtime/panic.go:489 +0x2cf
reflect.flag.mustBeAssignable(0x15)
/usr/lib/go/src/reflect/value.go:228 +0xf9
reflect.Value.Set(0x4b8b00, 0xc420, 0x15, 0x4b8b00, 0x0, 0x15)
/usr/lib/go/src/reflect/value.go:1345 +0x2f
json.(*decodeState).literalStore(0xc420084360, 0xc42000e3f8, 0x4, 0x8, 0x4b8b00, 0xc420, 0x15, 0xc420000100)
/home/kit/jstest/src/json/decode.go:883 +0x2797
json.(*decodeState).literal(0xc420084360, 0x4b8b00, 0xc420, 0x15)
/home/kit/jstest/src/json/decode.go:799 +0xdf
json.(*decodeState).value(0xc420084360, 0x4b8b00, 0xc420, 0x15)
/home/kit/jstest/src/json/decode.go:405 +0x32e
json.(*decodeState).unmarshal(0xc420084360, 0x4b8b00, 0xc420, 0x0, 0x0)
/home/kit/jstest/src/json/decode.go:184 +0x224
json.Unmarshal(0xc42000e3f8, 0x4, 0x8, 0x4b8b00, 0xc420, 0x8, 0x0)
/home/kit/jstest/src/json/decode.go:104 +0x148
main.main()
/home/kit/jstest/src/jstest/main.go:16 +0x1af
代碼導致該輸出:
package main
// Note "json" is the local copy of the "encoding/json" source that I modified.
import (
"fmt"
"json"
)
func main() {
for _, data := range []string{
`{"foo":"bar"}`,
`{}`,
`null`,
} {
m := make(map[string]string)
fmt.Printf("%#q: ", data)
if err := json.Unmarshal([]byte(data), m); err != nil {
fmt.Println(err)
} else {
fmt.Println(m == nil, m)
}
}
}
關鍵是這個這裏有點:
reflect.Value.Set using unaddressable value
因爲你通過地圖的副本,它是不可尋址(即它已經從低層次的機器角度的臨時地址,甚至無地址)。我知道一個解決方法(x := new(Type)
後跟*x = value
,除了使用reflect
包),但它實際上並沒有解決問題;你正在創建一個本地指針,無法返回給調用者並使用它來代替原始存儲位置!
所以現在嘗試指針:
if err := json.Unmarshal([]byte(data), m); err != nil {
fmt.Println(err)
} else {
fmt.Println(m == nil, m)
}
輸出:
`{"foo":"bar"}`: false map[foo:bar]
`{}`: false map[]
`null`: true map[]
現在,它的工作原理。底線:使用指針如果對象本身可能會被修改(和文檔說它可能是,例如,如果null
用於預期對象或數組(地圖或切片)的地方
我很喜歡這個答案。謝謝! – ollien