接口指针作为函数参数

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

Interface pointer as a function argument

问题

这很可能源于对Go语言中interface{}的误解。以下是你的代码:

type Configuration struct {
    Username string
}

func loadJson(jsonStr []byte, x *Configuration}) {
    json.Unmarshal(jsonStr, x)
}

func main() {
    //var config *Configuration
    config := new(Configuration)
    file, e := ioutil.ReadFile("config.json")
    loadJson(file, config)
    fmt.Printf("%s\n", config.Username)
}

它将一个JSON配置加载到config变量中。我想让loadJson函数更抽象,接受任何结构体。我认为最好的方法是接受*interface{},但是当我改变loadJson的签名时,我得到了以下错误:

./issue.go:30: cannot use config (type *Configuration) as type *interface {} in argument to loadJson:
	*interface {} is pointer to interface, not interface

相反,loadJson应该是这样的:

func loadJson(jsonStr []byte, x interface{}) {
    json.Unmarshal(jsonStr, x)
}

interface{}已经是一个指针吗?另外,错误消息对我来说并不是很清楚,难道Configuration不是指向接口的指针吗?另外,如果我将json.Unmarshal(jsonStr, x)更改为json.Unmarshal(jsonStr, &x),它仍然可以正常工作。这是怎么回事?

另外一个相关的问题是关于指针的,为什么我不能像main函数中的注释行那样声明一个指针呢?

英文:

This most likely stems from a misunderstanding of what interface{} is in go. I have the following code

type Configuration struct {
    Username string
}

func loadJson(jsonStr []byte, x *Configuration}) {
    json.Unmarshal(jsonStr, x)
}

func main() {
    //var config *Configuration
	config := new(Configuration)
    file, e := ioutil.ReadFile("config.json")
    loadJson(file, config)
    fmt.Printf("%s\n", config.Username)
}

It loads a json configuration into the config variable. I want to make the loadJson function more abstract and accept any struct. I thought the best way to do that would be to accept a *interface{}, however I get the following error when changing the loadJson Signature.

 ./issue.go:30: cannot use config (type *Configuration) as type *interface {} in argument to loadJson:
*interface {} is pointer to interface, not interface

Instead load json should be this

func loadJson(jsonStr []byte, x interface{}}) {
    json.Unmarshal(jsonStr, x)
}

Is interface{} already a pointer? Also the error message doesn't make the most sense to me, isn't configuration a pointer to an interface? Also, if I change json.Unmarshal(jsonStr, x) to json.Unmarshal(jsonStr, &x) it will work perfectly fine still. What is going on here that allows that to work?

Side question but relevant to pointers, why can't I declare a pointer like the commented out line(under main)?

答案1

得分: 4

使用interface{}来表示任何类型,包括指针:

func loadJson(jsonStr []byte, x interface{}) {
    json.Unmarshal(jsonStr, x)
}

虽然你可以将Configuration赋值给interface{},但是Configuration值和interface{}值的内存布局是不同的。由于内存布局不同,无法将interface{}的指针转换为Configuration的指针。同样的原因也适用于[]T[]interface{}

在Go语言中很少使用指向接口的指针。

关于附注:你可以使用变量声明和赋值:

var config *Configuration
config = new(Configuration)

或者你可以使用短变量声明:

config := new(Configuration)

你不能同时使用声明和短变量声明,因为这会导致变量声明两次。

英文:

Use interface{} to represent any type including pointers:

func loadJson(jsonStr []byte, x interface{}) {
   json.Unmarshal(jsonStr, x)
}

<kbd>playground</kbd>

Although you can assign a Configuration to an interface{}, the memory layout of a Configuration value and an interface{} value are different. Because the memory layouts are different, a pointer to an interface{} cannot be converted to a pointer to a Configuration. The same reasoning applies to a []T and a []interface[}.

It's rare to user a pointer to an interface in Go.

Regarding the side note: You can use a variable declaration and assignment

var config *Configuration
config = new(Configuration)

or you can use a short variable declaration:

config := new(Configuration)

You cannot use declaration and short declaration together because it declares the variable twice.

答案2

得分: 2

interface{}是一个特殊的类型,它可以表示任意类型的值。在Go语言中,接口类型是一种多态的实现方式。当一个值实现了某个接口时,它可以被包装在该接口类型的变量中。

在代码中,*Configuration*interface{}都是指针类型,但它们是完全不同的类型。即使它们在内存中的表示方式可能相同,你也不能将*int赋值给*interface{},因为它们是不兼容的类型。

关于为什么interface{}可以接受任意类型的值,这是因为接口类型在实现上有所特殊。在接口类型中,值可以被“装箱”成接口值,只要它实现了该接口。

需要注意的是,接口值在底层有时会持有一个指针,但这是实现细节,在这里并不相关。

参考链接:http://play.golang.org/p/EbGCn8F7op

英文:

> Is interface{} already a pointer?

No, not in general <sup>[1]</sup>.

> What is going on here that allows that to work?

Consider the following type definition:

type Number int

int and Number are now two completely distinct types. You can't use a *int where a *Number is expected; even if they are essentially the same, even memory-wise.

The rule is the same for *Configuration and *interface{}; even if their memory representation was identical (which it isn't).

Why does it work for interface{} then? Because interface-types are special; they are Go's way of doing polymorphism. Any value can be "boxed" in an interface value if it implements said interface.

<sup>[1] Under the hood, an interface value sometimes holds a pointer but that's an implementation detail and not relevant here.</sup>

答案3

得分: 1

一个指向接口的指针与一个指向配置的指针具有不同的内存布局。它们不能互换使用,你不能使用*interface{}来表示任何指针。将接口值想象成一个盒子。你想要将json函数传递一个包含指针的盒子,而不是一个指向盒子的指针。

如果你想要了解接口的底层表示,请参考http://research.swtch.com/interfaces。

英文:

A pointer to an interface has a different memory layout than a pointer to a Configuration. They are not interchangeable and you can't use *interface{} to represent any pointer. Think of an interface value like a box. The idea is you want to pass the json function a box containing a pointer and not a pointer to a box.

If you want to understand the underlying representation of an interface, see http://research.swtch.com/interfaces.

huangapple
  • 本文由 发表于 2014年10月15日 07:12:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/26371938.html
匿名

发表评论

匿名网友

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

确定