Golang接口{}类型的误解 [英] Golang interface{} type misunderstanding

查看:148
本文介绍了Golang接口{}类型的误解的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当使用接口{} 作为函数参数类型时,在给出非指针类型并使用 json时,我在Go中遇到了一个bug 。

因为一段代码胜过千言万语,下面是一个例子:

  package main 

import(
encoding / json
fmt


func test(i interface {}){
j:= [] byte(`{foo:bar}`)
fmt.Printf(%T \ n,i)
fmt.Printf(%T \ n,& i)
json.Unmarshal(j,& i)
fmt.Printf( %T \ n,i)
}

类型测试结构{
Foo字符串
}

func main(){
test(Test {})
}

输出:

  main.Test 
* interface {}
map [string] interface {}

json.Unmarshal 将我的结构转换为映射[string] interface {} oO ...



小读数后面解释了一些, interface {} 本身就是一种类型,而不是某种无类型的容器,它解释了 * interface {} ,事实上 json.Unmarshal 无法获得初始类型,并返回了一个 map [string] interface {} ..



Unmarshal 文档:


要将JSON解组为接口值,Unmarshal会将
中的一个存储在接口值中:
[...]


如果我像这样传递一个指向测试函数的指针,它可以工作:

  func test(i interface {}){
j:= [] byte(`{foo:bar}`)
fmt.Printf(%T \ n,i)
fmt.Printf(%T \ n,& i)
json.Unmarshal(j,i)
fmt.Printf(%T \ n),i)
fmt.Println(i)
}

func main(){
test(& Test {})
}

以下输出:

  * main.Test 
* interface {}
* main.Test
& {bar}

酷,数据已解组,所有,现在在第二个片段中,当调用 Unmarshal 时,我移除了& 。因为我在 i 中有一个 * Test ,所以没有用。



因此,在所有逻辑中,如果我在调用<$ c时将& 放回到 i $ c> Unmarshal 它应该再次与 i 的类型混淆。但是不是。



如果我运行:

  func test(i接口{}){
j:= []字节(`{foo:bar}`)
fmt.Printf(%T \ n,i)
fmt .Printf(%T \ n,& i)
json.Unmarshal(j,& i)
fmt.Printf(%T \ n,i)
$ f


func main(){
test(& Test {})
}



它仍然有效:

  * main.Test 
* interface {}
* main.Test
& {bar}

现在我不在google搜索查询中。 正确的方案 >

interface {} 是任何值和任何类型的包装。一个接口示意性地包装了一个(value; type)对,一个具体的值及其类型。更多细节:反思的法则#接口的表示

json.Unmarshal() 已经取得类型 interface { }
$ b

 func Unmarshal(data [] byte,v interface {})错误

因此,如果您已经有界面{} 值( test()函数的 i interface {} 参数,不要试图获取它的地址,只是还要注意,对于任何包来修改存储在 interface {}中的值 >,你需要传递一个指针给它。那么 i 应该是一个指针。因此,正确的方案是将 * Test 传递给 test(),并在 test )传递 i json.Unmarshal()(不需要它的地址) / b>

其他情况的解释



i 包含 * Test 并且您传递了& i ,它会工作,因为 json package将简单地解引用 * interface {} 指针,并找到一个 interface {} 值,一个 * Test 值。它是一个指针,所以它很好:将JSON对象解组到指向 Test 值。



i 包含 Test 并且您传递& i ,同样的事情发生如上所示: * interface {} 被取消引用,所以它找到一个包含非指针的 interface {} 测试。由于 json 包无法解组为非指针值,因此必须创建一个新值。并且由于传递给 json.Unmarshal()函数的值的类型是 * interface {} ,它会告诉 json package将数据解组为一个值为 interface {} 的值。这意味着 json 包可以自由选择使用哪种类型。默认情况下, json 包将JSON对象解组为 map [string] interface {} 值,这就是创建的并使用(并最终放入您传递的指针指向的值:& i )。



全部在所有



总而言之,避免使用指向接口的指针。而是将指针放入接口(接口值应该包装指针)。当你已经有一个接口{} 存储一个指针时,只需传递它即可。


I got a bug in Go when using an interface{} as function parameter type, when given a non-pointer type, and using json.Unmarshal with it.

Because a piece of code is worth a thousand words, here is an example:

package main

import (
    "encoding/json"
    "fmt"
)

func test(i interface{}) {
    j := []byte(`{ "foo": "bar" }`)
    fmt.Printf("%T\n", i)
    fmt.Printf("%T\n", &i)
    json.Unmarshal(j, &i)
    fmt.Printf("%T\n", i)
}

type Test struct {
    Foo string
}

func main() {
    test(Test{})
}

Which outputs:

main.Test
*interface {}
map[string]interface {}

json.Unmarshal turns my struct to a map[string]interface{} oO...

Little readings later explains some of it, interface{} is a type in itself, not some sort of typeless container, which explains the *interface{}, and the fact that json.Unmarshal could not get the initial type, and returned a map[string]interface{}..

From Unmarshal docs:

To unmarshal JSON into an interface value, Unmarshal stores one of these in the interface value: [...]

And if I pass a pointer to the test function like so, it works:

func test(i interface{}) {
    j := []byte(`{ "foo": "bar" }`)
    fmt.Printf("%T\n", i)
    fmt.Printf("%T\n", &i)
    json.Unmarshal(j, i)
    fmt.Printf("%T\n", i)
    fmt.Println(i)
}

func main() {
    test(&Test{})
}

Which outputs:

*main.Test
*interface {}
*main.Test
&{bar}

Cool, the data is unmarshalled and all, now in this second snippet I removed the & when calling Unmarshal. Because I have a *Test in i, no use for it.

So in all logic, if I put back the & to i when calling Unmarshal it should mess up with i's type again. But no.

If I run:

func test(i interface{}) {
    j := []byte(`{ "foo": "bar" }`)
    fmt.Printf("%T\n", i)
    fmt.Printf("%T\n", &i)
    json.Unmarshal(j, &i)
    fmt.Printf("%T\n", i)
    fmt.Println(i)
}

func main() {
    test(&Test{})
}

Well it still works:

*main.Test
*interface {}
*main.Test
&{bar}

And now I'm out of google search queries.

解决方案

The right scenario

interface{} is a wrapper for any value and of any type. An interface schematically wraps a (value; type) pair, a concrete value and its type. More details on this: The Laws of Reflection #The representation of an interface.

json.Unmarshal() already takes the value of type interface{}:

func Unmarshal(data []byte, v interface{}) error

So if you already have an interface{} value (the i interface{} parameter of the test() function), don't try to take its address, just pass it along as-is.

Also note that for any package to modify a value stored in an interface{}, you need to pass a pointer to it. So what should be in i is a pointer. So the right scenario is to pass *Test to test(), and inside test() pass i to json.Unmarshal() (without taking its address).

Explanation of other scenarios

When i contains *Test and you pass &i, it will work because the json package will simply dereference the *interface{} pointer, and finds an interface{} value, which wraps a *Test value. It's a pointer, so it's all good: unmarshals the JSON object into the pointed Test value.

When i contains Test and you pass &i, same thing goes as above: *interface{} is dereferenced, so it finds an interface{} which contains a non-pointer: Test. Since the json package can't unmarshal into a non-pointer value, it has to create a new value. And since the passed value to the json.Unmarshal() function is of type *interface{}, it tells the json package to unmarshal the data into a value of type interface{}. This means the json package is free to choose which type to use. And by default the json package unmarshals JSON objects into map[string]interface{} values, so that is what's created and used (and eventually put into the value pointed by the pointer you passed: &i).

All in all

All in all, avoid using pointers to interfaces. Instead "put" pointers into the interfaces (the interface value should wrap the pointer). When you already have an interface{} holding a pointer, just pass it along.

这篇关于Golang接口{}类型的误解的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆