美文网首页
从json.Marshal到nil Pointer

从json.Marshal到nil Pointer

作者: MontyOak | 来源:发表于2020-07-19 21:54 被阅读0次

    原文链接: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!!!
    }
    

    这是一段并不复杂的代码:尝试将一段文本反序列化到一个go 结构体,示例中给出四种定义,其中只有一种情况发生了报错,我们接下来就从这个异常情况来简单说下go语言体系中,空指针的概念。
    在上面代码示例中,发生报错的res4res2从数据类型上来说并无不同,都是一个指向Result结构体的指针。我们来简单看下发生报错时我们拿到的提示json: Unmarshal(nil *main.Result)。看上去结果比较清晰,json.Unmarshal并不接受空指针(nil pointer)。
    事实是这样吗?我们去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
    )

    果然,文档完善的标准库给了我们足够清晰的结论:对于非指针和空指针的参数,会抛出InvalidUnmarshalError
    那么,encoding/json的接口又是为什么这么设计呢?这里我们要先清楚以下两个前提:

    1. go中的赋值操作都是传值的,这里的赋值包括变量的定义初始化,变量的绑定,函数参数传递。
    2. go的空指针和指向空值的指针有本质上区别:指向空值的指针本身指向的是一个空的对象(取决于指针的类型),被指向的对象本身是一个已经分配好的空间;空指针就是指针的空值nil,本身不指向任一对象。

    基于以上两个前提,我们再来看下Unmarshal的函数定义:

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

    函数本身的返回值并不包含反序列之后的对象,只有一个描述结果的error返回值。所以Unmarshal的函数行为一定是通过改写v对象来达到的。根据第一条前提,我们知道如果v本身是非指针的话,Unmarshal的改写行为无法影响传递进来的值对象的原始值;又根据第二条前提,v如果是nil指针的话,函数无法根据指针去改写它指向的对象。
    一个Unmarshal的文档描述,引出了go语言的两个知识点,还是挺有趣的。

    相关文章

      网友评论

          本文标题:从json.Marshal到nil Pointer

          本文链接:https://www.haomeiwen.com/subject/zsnrkktx.html