考虑以下结构和接口定义。
type Foo interface {
Operate()
}
type Bar struct {
A int
}
func (b Bar) Operate() {
//...
}
现在,如果我们尝试执行以下操作(playground):
var x Foo = Bar{}
err := json.Unmarshal([]byte("{\"a\": 5}"), &x)
fmt.Printf("x: %+v\nerr: %s\n", x, err)
我们得到以下输出:
x: {A:0}
err: json: cannot unmarshal object into Go value of type main.Foo
但是,通过使用结构类型替换基础数据,它可以顺便进行(playground):
var x Foo = &Bar{}
err := json.Unmarshal([]byte("{\"a\": 5}"), &x)
fmt.Printf("x: %+v\nerr: %s\n", x, err)
输出:
x: &{A:5}
err: %!s(<nil>)
然而,这对我来说非常困惑。在我们对Unmarshall的调用中,我们仍然传递一个指向x的指针,根据我的理解,这应该足以让我们修改下面的Bar。毕竟,指针只是内存中的地址。如果我们通过该地址,我们应该可以修改它,不是吗?为什么第二个例子有效,但不是第一个?为什么结构作为指针会产生重大影响?
答案 0 :(得分:3)
不同行为的根源在于,如果json
包必须创建新值,则该值必须是具体类型,并且不能是(非具体)接口类型。
让我们详细说明一下。
首先让我们来看看你的第二个工作实例。类型为x
的变量Foo
包含类型为*Bar
的指针值。当您将&x
传递给json.Unmarshal()
时,json
包将获得*Foo
指针值。指向界面的指针!不应该使用,但它是。 json
包将取消引用指针,并获取类型为*Bar
的包装值。由于这是一个非nil
指针,json
包可以 - 并且将继续使用它来进行解组。都好! json
包将修改指向的值。
你的第一个例子会发生什么?
在第一个示例中,类型为x
的{{1}}变量包装了非指针结构值。 无法修改界面中包含的值。
这是什么意思? Foo
包将再次收到json
类型的值。然后它继续并取消引用它,并获得一个包含在接口中的*Foo
值。无法修改Bar
接口内的Bar
值。 Foo
包“传递”结果的唯一方法是创建一个实现json
的新值,并将此值存储在最初传递的Foo
指向的位置。但是无法创建*Foo
的值,它是非具体的接口类型。所以unmarshal会返回错误。
一些附录。你的第二个工作实例:
Foo
这是有效的,因为我们“准备”非var x Foo = &Bar{}
err := json.Unmarshal([]byte("{\"a\": 5}"), &x)
fmt.Printf("x: %+v\nerr: %s\n", x, err)
nil
值来解组,因此*Bar
包不必自己创建值。我们已经准备并传递了json
接口类型的值(是具体类型Foo
的值)。
如果我们存储*Bar
中指向的“类型”nil
,请执行以下操作:
x
我们会收到相同的错误(在Go Playground上尝试):
var x Foo = &Bar{}
x = (*Bar)(nil)
err := json.Unmarshal([]byte("{\"a\": 5}"), &x)
fmt.Printf("x: %+v\nerr: %s\n", x, err)
解释是一样的:x: <nil>
err: json: cannot unmarshal object into Go value of type main.Foo
包不能使用json
指针来解组值。它还需要创建一个非具体类型nil
的值,但这是不可能的。只能创建具体类型的值。