從json.Marshal到nil Pointer

原文鏈接:Dave Cheney的博文
先從一段代碼說起:

package main

import (
    "encoding/json"
    "fmt"
)

type Result struct {
    Foo string
}

func main() {
    content := `{"foo": "bar"}`

    res := &Result{}
    err := json.Unmarshal([]byte(content), &res)
    if err != nil {
        panic(err)
    }
    fmt.Printf("res  = %+v\n", res) // 正常返回

    res2 := &Result{}
    err = json.Unmarshal([]byte(content), res2)
    if err != nil {
        panic(err)
    }
    fmt.Printf("res2 = %+v\n", res2) // 正常返回

    var res3 *Result
    err = json.Unmarshal([]byte(content), &res3)
    if err != nil {
        panic(err)
    }
    fmt.Printf("res3 = %+v\n", res3) // 正常返回

    var res4 *Result
    err = json.Unmarshal([]byte(content), res4)
    if err != nil {
        panic(err)
    }
    fmt.Printf("res4 = %+v\n", res4) // panic!?。?}

這是一段并不復(fù)雜的代碼:嘗試將一段文本反序列化到一個go 結(jié)構(gòu)體,示例中給出四種定義,其中只有一種情況發(fā)生了報錯,我們接下來就從這個異常情況來簡單說下go語言體系中,空指針的概念。
在上面代碼示例中,發(fā)生報錯的res4res2從數(shù)據(jù)類型上來說并無不同,都是一個指向Result結(jié)構(gòu)體的指針。我們來簡單看下發(fā)生報錯時我們拿到的提示json: Unmarshal(nil *main.Result)??瓷先ソY(jié)果比較清晰,json.Unmarshal并不接受空指針(nil pointer)。
事實是這樣嗎?我們?nèi)?code>encoding/json的文檔中查看一下:

Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v. If v is nil or not a pointer, Unmarshal returns an InvalidUnmarshalError.(https://pkg.go.dev/encoding/json?tab=doc#Unmarshal
)

果然,文檔完善的標(biāo)準(zhǔn)庫給了我們足夠清晰的結(jié)論:對于非指針和空指針的參數(shù),會拋出InvalidUnmarshalError。
那么,encoding/json的接口又是為什么這么設(shè)計呢?這里我們要先清楚以下兩個前提:

  1. go中的賦值操作都是傳值的,這里的賦值包括變量的定義初始化,變量的綁定,函數(shù)參數(shù)傳遞。
  2. go的空指針和指向空值的指針有本質(zhì)上區(qū)別:指向空值的指針本身指向的是一個空的對象(取決于指針的類型),被指向的對象本身是一個已經(jīng)分配好的空間;空指針就是指針的空值nil,本身不指向任一對象。

基于以上兩個前提,我們再來看下Unmarshal的函數(shù)定義:

func Unmarshal(data []byte, v interface{}) error

函數(shù)本身的返回值并不包含反序列之后的對象,只有一個描述結(jié)果的error返回值。所以Unmarshal的函數(shù)行為一定是通過改寫v對象來達(dá)到的。根據(jù)第一條前提,我們知道如果v本身是非指針的話,Unmarshal的改寫行為無法影響傳遞進(jìn)來的值對象的原始值;又根據(jù)第二條前提,v如果是nil指針的話,函數(shù)無法根據(jù)指針去改寫它指向的對象。
一個Unmarshal的文檔描述,引出了go語言的兩個知識點,還是挺有趣的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容