douwo6738 2017-07-15 20:33
浏览 577
已采纳

如果地图是引用类型,为什么json.Unmarshal需要指向地图的指针?

I was working with json.Unmarshal and came across the following quirk. When running the below code, I get the error json: Unmarshal(non-pointer map[string]string)

func main() {
    m := make(map[string]string)
    data := `{"foo": "bar"}`
    err := json.Unmarshal([]byte(data), m)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(m)
}

Playground

Looking at the documentation for json.Unmarshal, there is seemingly no indication that a pointer is required. The closest I can find is the following line

Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v.

The lines regarding the protocol Unmarshal follows for maps are similarly unclear, as it makes no reference to pointers.

To unmarshal a JSON object into a map, Unmarshal first establishes a map to use. If the map is nil, Unmarshal allocates a new map. Otherwise Unmarshal reuses the existing map, keeping existing entries. Unmarshal then stores key-value pairs from the JSON object into the map. The map's key type must either be a string, an integer, or implement encoding.TextUnmarshaler.

Why must I pass a pointer to json.Unmarshal, especially if maps are already reference types? I know that if I pass a map to a function, and add data to the map, the underlying data of the map will be changed (see the following playground example), which means that it shouldn't matter if I pass a pointer to a map. Can someone clear this up?

  • 写回答

3条回答 默认 最新

  • du77887 2017-07-15 22:40
    关注

    As stated in the documentation:

    Unmarshal uses the inverse of the encodings that Marshal uses, allocating maps, slices, and pointers as necessary, with ...

    Unmarshal may allocates the variable(map, slice, etc.). If we pass a map instead of pointer to a map, then the newly allocated map won't be visible to the caller. The following examples (Go Playground) demonstrates this:

    package main
    
    import (
        "fmt"
    )
    
    func mapFunc(m map[string]interface{}) {
        m = make(map[string]interface{})
        m["abc"] = "123"
    }
    
    func mapPtrFunc(mp *map[string]interface{}) {
        m := make(map[string]interface{})
        m["abc"] = "123"
    
        *mp = m
    }
    
    func main() {
        var m1, m2 map[string]interface{}
        mapFunc(m1)
        mapPtrFunc(&m2)
    
        fmt.Printf("%+v, %+v
    ", m1, m2)
    }
    

    in which the output is:

    map[], map[abc:123]
    

    If the requirement says that a function/method may allocate a variable when necessary and the newly allocated variable need to be visible to the caller, the solution will be: (a) the variable must be in function's return statement or (b) the variable can be assigned to the function/method argument. Since in go everything is pass by value, in case of (b), the argument must be a pointer. The following diagram illustrates what happen in the above example:

    Illustration of variable allocation

    1. At first, both map m1 and m2 point to nil.
    2. Calling mapFunc will copy the value pointed by m1 to m resulting m will also point to nil map.
    3. If in (1) the map already allocated, then in (2) the address of underlying map data structure pointed by m1 (not the address of m1) will be copied to m. In this case both m1 and m point to the same map data structure, thus modifying map items through m1 will also be visible to m.
    4. In the mapFunc function, new map is allocated and assigned to m. There is no way to assign it to m1.

    In case of pointer:

    1. When calling mapPtrFunc, the address of m2 will be copied to mp.
    2. In the mapPtrFunc, new map is allocated and assigned to *mp (not mp). Since mp is pointer to m2, assigning the new map to *mp will change the value pointed by m2. Note that the value of mp is unchanged, i.e. the address of m2.
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥20 关于#c++#的问题:水果店管理系统
  • ¥30 dbLinq最新版linq sqlite
  • ¥20 对D盘进行分盘之前没有将visual studio2022卸载掉,现在该如何下载回来
  • ¥15 完成虚拟机环境配置,还有安装kettle
  • ¥15 2024年全国大学生数据分析大赛A题:直播带货与电商产品的大数据分析 问题5. 请设计一份优惠券的投放策略,需要考虑优惠券的数量、优惠券的金额、投放时间段和投放商品种类等因素。求具体的python代码
  • ¥15 有人会搭建生鲜配送自营+平台的管理系统吗
  • ¥15 用matlab写代码
  • ¥30 motoradmin系统的多对多配置
  • ¥15 求组态王串口自定义通信配置方法或代码?
  • ¥15 实验 :UML2.0 结构建模