英文:
Why does json.Unmarshal work with reference but not pointer?
问题
这是一个来自 json.Unmarshal 文档的示例(稍作修改以简化为使用 Animal
而不是 []Animal
),它可以正常工作,没有错误:
// ...
var animals Animal
err := json.Unmarshal(jsonBlob, &animals)
// ...
但是这个稍作修改的示例却不能正常工作:
// ...
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论