Pointers 指向接口的指针与在解组JSON时持有指针的接口

Pointers 指向接口的指针与在解组JSON时持有指针的接口,pointers,go,interface,unmarshalling,Pointers,Go,Interface,Unmarshalling,请考虑以下结构和接口定义 type Foo interface { Operate() } type Bar struct { A int } func (b Bar) Operate() { //... } 现在,如果我们尝试执行以下()操作: 我们得到以下输出: x: {A:0} err: json: cannot unmarshal object into Go value of type main.Foo 但是,通过将基础数据替换为struct类型,它不会

请考虑以下结构和接口定义

type Foo interface {
    Operate()
}

type Bar struct {
    A int
}

func (b Bar) Operate() {
    //...
}
现在,如果我们尝试执行以下()操作:

我们得到以下输出:

x: {A:0}
err: json: cannot unmarshal object into Go value of type main.Foo
但是,通过将基础数据替换为struct类型,它不会有任何问题():

输出:

x: &{A:5}    
err: %!s(<nil>)
x:&{A:5}
错误:%!s()

然而,这让我很困惑。在调用Unmarshall时,我们仍然传递一个指向x的指针,据我所知,它应该足以允许我们修改下面的条。毕竟,指针只是内存中的地址。如果我们要传递那个地址,我们应该可以修改它,不是吗?为什么第二个例子有效,但第一个却不行?为什么作为指针的结构会产生所有差异?

不同行为的根源在于,如果
json
包必须创建一个新值,那么该值必须是具体类型,不能是(非具体)接口类型

让我们详细说明一下

首先,让我们检查第二个工作示例。类型为
Foo
的变量
x
包装类型为
*Bar
的指针值。当您将
&x
传递给
json.Unmarshal()
时,
json
包将获得一个
*Foo
指针值。指向接口的指针!不应该使用,但它在那里。
json
包将取消对指针的引用,并获得类型为
*Bar
的包装值。由于这是一个非
nil
指针,因此
json
包可以并且将继续使用它进行解组。一切都好!
json
包将修改所指向的值

在您的第一个示例中发生了什么

在第一个示例中,类型为
Foo
x
变量包装了一个非指针结构值不能修改包装在接口中的值。

这是什么意思?
json
包将再次接收类型为
*Foo
的值。然后它继续并取消引用它,并获得一个包装在接口中的
值。无法修改
Foo
界面中的
值。
json
包“交付”结果的唯一方法是创建一个实现
Foo
的新值,并将该值存储在最初传递的
*Foo
指向的位置。但是不能创建
Foo
的值,它是一种非具体的接口类型。所以解组返回一个错误

一些附录。您的第二个工作示例:

var x Foo = &Bar{}
err := json.Unmarshal([]byte("{\"a\": 5}"), &x)
fmt.Printf("x: %+v\nerr: %s\n", x, err)
这是可行的,因为我们“准备”了一个非
nil
*条
值来解组,所以
json
包本身不必创建值。我们已经为
Foo
接口类型准备并传递了一个值(具体类型为
*Bar

如果我们存储一个指向
x
的“键入的”
nil
,如下所示:

var x Foo = &Bar{}
x = (*Bar)(nil)
err := json.Unmarshal([]byte("{\"a\": 5}"), &x)
fmt.Printf("x: %+v\nerr: %s\n", x, err)
我们将返回相同的错误(在上尝试):

x:
错误:json:无法将对象解组为main.Foo类型的Go值

解释是一样的:
json
包不能使用
nil
指针将值解组到。它还需要创建一个非具体类型的值
Foo
,但这是不可能的。只能创建具体类型的值。

不同行为的根源在于,如果
json
包必须创建新值,则该值必须是具体类型,不能是(非具体)接口类型

让我们详细说明一下

首先,让我们检查第二个工作示例。类型为
Foo
的变量
x
包装类型为
*Bar
的指针值。当您将
&x
传递给
json.Unmarshal()
时,
json
包将获得一个
*Foo
指针值。指向接口的指针!不应该使用,但它在那里。
json
包将取消对指针的引用,并获得类型为
*Bar
的包装值。由于这是一个非
nil
指针,因此
json
包可以并且将继续使用它进行解组。一切都好!
json
包将修改所指向的值

在您的第一个示例中发生了什么

在第一个示例中,类型为
Foo
x
变量包装了一个非指针结构值不能修改包装在接口中的值。

这是什么意思?
json
包将再次接收类型为
*Foo
的值。然后它继续并取消引用它,并获得一个包装在接口中的
值。无法修改
Foo
界面中的
值。
json
包“交付”结果的唯一方法是创建一个实现
Foo
的新值,并将该值存储在最初传递的
*Foo
指向的位置。但是不能创建
Foo
的值,它是一种非具体的接口类型。所以解组返回一个错误

一些附录。您的第二个工作示例:

var x Foo = &Bar{}
err := json.Unmarshal([]byte("{\"a\": 5}"), &x)
fmt.Printf("x: %+v\nerr: %s\n", x, err)
这是可行的,因为我们“准备”了一个非
nil
*条
值来解组,所以
json
包本身不必创建值。我们已经为
Foo
接口类型准备并传递了一个值(具体类型为
*Bar

如果我们存储一个指向
x
的“键入的”
nil
,如下所示:

var x Foo = &Bar{}
x = (*Bar)(nil)
err := json.Unmarshal([]byte("{\"a\": 5}"), &x)
fmt.Printf("x: %+v\nerr: %s\n", x, err)
我们将返回相同的错误(在上尝试):

x:
错误:json:无法将对象解组为main.Foo类型的Go值
解释是一样的:
json
包不能使用
nil
指针将值解组到。它还需要创建一个非具体类型的值
x: <nil>
err: json: cannot unmarshal object into Go value of type main.Foo