为什么 json.Unmarshal 能够使用引用而不是指针?

huangapple go评论96阅读模式
英文:

Why does json.Unmarshal work with reference but not pointer?

问题

这是一个来自 json.Unmarshal 文档的示例(稍作修改以简化为使用 Animal 而不是 []Animal),它可以正常工作,没有错误:

可工作示例的 Playground 链接

// ...
var animals Animal
err := json.Unmarshal(jsonBlob, &animals)
// ...

但是这个稍作修改的示例却不能正常工作:

不可工作示例的 Playground 链接

// ...
var animals *Animal
err := json.Unmarshal(jsonBlob, animals)
// ...

它显示了这个晦涩的错误,实际上并没有提供太多帮助(在我看来更像是一个函数调用而不是错误):

> json: Unmarshal(nil *main.Animal)

这似乎是因为 animals 是一个未初始化的指针。但是文档中说(重点在于):

> Unmarshal 将 JSON 解组到指针所指向的值。如果指针为 nil,Unmarshal 会为其分配一个新值。

那么为什么在第二个示例中解组失败并显示这个晦涩的错误呢?

(另外,“unmarshalling” 还是 “unmarshaling”(一个 L)?文档中两者都有使用。)

英文:

This example from the json.Unmarshal docs (slightly modified for simplicity to use Animal instead of []Animal) works, no errors:

Playground link of working example

// ...
var animals Animal
err := json.Unmarshal(jsonBlob, &animals)
// ...

But this slightly modified example doesn't:

Playground link of non-working example

// ...
var animals *Animal
err := json.Unmarshal(jsonBlob, animals)
// ...

It displays this obscure error that really isn't helpful (looks more like a function call than an error IMO):

> json: Unmarshal(nil *main.Animal)

This appears to be because animals is an uninitialized pointer. But the docs say (emphasis mine):

> Unmarshal unmarshals the JSON into the value pointed at by the pointer. If the pointer is nil, Unmarshal allocates a new value for it to point to.

So why does unmarshaling fail in the second example and show that obscure error?

(Also, is it "unmarshalling" or "unmarshaling" (one L)? The docs use both.)

答案1

得分: 54

你遇到了一个InvalidUnmarshalError(请参见decode.go中的109和110行)。

// InvalidUnmarshalError描述了传递给Unmarshal的无效参数。
// (Unmarshal的参数必须是非nil指针。)

看起来文档可能需要一些澄清,因为上面的引用和下面的注释(来自Unmarshal源代码的这里)似乎相互矛盾。

// 如果指针为nil,Unmarshal会为其分配一个新值。

英文:

You've encountered an InvalidUnmarshalError (see lines 109 and 110 in decode.go).

>// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal.
// (The argument to Unmarshal must be a non-nil pointer.)

It seems the docs could do with some clarification as the quote above and the comment below from the Unmarshal source seem to contradict each other.

>If the pointer is nil, Unmarshal allocates a new value for it to point to.

答案2

得分: 52

因为你的指针是空的。

如果你初始化它,它就会工作:http://play.golang.org/p/zprmV0O1fG

var animals *Animal = &Animal{}

此外,它可以以任何一种方式拼写(尽管在单个文档中保持一致性会更好):http://en.wikipedia.org/wiki/Marshalling_(computer_science)

英文:

Because your pointer is nil.

If you initialize it it works: http://play.golang.org/p/zprmV0O1fG

var animals *Animal = &Animal{}

Also, it can be spelled either way (consistency in a single doc would be nice, though): http://en.wikipedia.org/wiki/Marshalling_(computer_science)

答案3

得分: 21

我认为问题在于,虽然你可以将一个指向nil的指针传递给Unmarshal()函数,但你不能传递一个空指针值。

一个指向nil的指针可能是这样的:

var v interface{}
json.Unmarshal(text, &v)

v的值是nil,但指向v的指针是一个非零指针地址。它是一个非零指针,指向一个nil的interface{}(它本身是一个指针类型)。在这种情况下,Unmarshal()函数不会返回错误。

一个空指针可能是这样的:

var v *interface{}
json.Unmarshal(text, v)

在这种情况下,v的类型是指向interface{}的指针,但是和在golang中声明任何变量一样,v的初始值是该类型的零值。所以v是一个零值指针,这意味着它没有指向内存中的任何有效位置。

正如在https://stackoverflow.com/a/20478917/387176中提到的,json.Unmarshal()函数需要一个有效的指针指向某个东西,这样它才能在原地更改这个东西(无论是一个零值结构体还是一个指针)。

英文:

I believe the issue is that, while you can pass a pointer to nil to Unmarshal(), you can't pass a nil pointer value.

A pointer to nil would be like:

var v interface{}
json.Unmarshal(text, &v)

The value of v is nil, but the pointer to v is a non-zero pointer address. It's a non-zero pointer, which is pointing to a nil interface{} (which itself is a pointer type). Unmarshal doesn't return an error in this case.

A nil pointer would be like:

var v *interface{}
json.Unmarshal(text, v)

In this case, the type of v is pointer to an interface{}, but as with any declaration of a var in golang, the initial value of v is the type's zero-value. So v is a zero-value pointer, which means it isn't pointing to any valid place in memory.

As mentioned in the https://stackoverflow.com/a/20478917/387176, json.Unmarshal() needs a valid pointer to something, so it can change the something (be it a zero value struct, or a pointer) in place.

答案4

得分: 1

我以前遇到过类似的情况,但是是在不同的情况下。这与Go语言中的接口概念有关。如果一个函数声明了一个接口作为参数或返回值,调用者必须传递或返回引用

在你的情况下,json.Unmarshal接受接口作为第二个参数。

英文:

I had a similiar condition before but in a different case.
It is related with concept of interface in Go.
If a function declares a interface as argument or return value, the caller have to pass or return the reference

In your case, json.Unmarshal accept interface as second argument

huangapple
  • 本文由 发表于 2013年12月10日 03:03:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/20478577.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定