使用Go语言的结构体指针作为接口的用法

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

Use of go's struct pointer as interface

问题

我想将结构体方法作为函数值传递。为什么如果函数要求返回interface{},而它返回struct,编译会失败?如果我尝试从声明为返回interface{}的函数(包装函数)返回struct,它完全可以工作。

package main

func main() {
    println("hello")
    testInterface(wrapper)          // 可以工作
    instance := MyStruct{}
    testInterface(instance.works)   // 可以工作
    testInterface(instance.fails)   // 失败:./main.go:8: cannot use instance.fails (type func(int) *MyStruct) as type func(int) interface {} in argument to testInterface
}

func testInterface(f func() interface{}) {
    f()
    return
}

type MyStruct struct {
}

func (s *MyStruct) works() interface{} {
    return s
}

func (s *MyStruct) fails() *MyStruct {
    return s
}

func wrapper() interface{} {
    s := MyStruct{}
    return s.fails()
}

编译失败是因为testInterface函数要求传入的函数类型是func() interface{},而instance.fails的类型是func(int) *MyStruct,二者不匹配。你可以尝试将instance.fails的返回类型更改为interface{},这样就可以通过编译了。

英文:

I want to pass struct method as function value. Why does compilation fail if function is required to return interface{} and it returns *struct? It perfectly works if I try to return *struct from function that is declared to return interface{} (wrapper func).

package main

func main() {
        println("hello")
        testInterface(wrapper)          // works
        instance := MyStruct{}
        testInterface(instance.works)   // works
        testInterface(instance.fails)   // fails: ./main.go:8: cannot use instance.fails (type func(int) *MyStruct) as type func(int) interface {} in argument to testInterface
}

func testInterface(f func() interface{}) {
        f()
        return
}

type MyStruct struct {
}

func (s *MyStruct) works() interface{} {
        return s
}

func (s *MyStruct) fails() *MyStruct {
        return s
}

func wrapper() interface{} {
        s := MyStruct{}
        return s.fails()

}

答案1

得分: 4

这是要翻译的内容:

这是因为它不符合可赋值性的条件。

  • 如果满足以下任一情况,值x可以赋值给类型T的变量("x可以赋值给T"):
    • x的类型与T完全相同。
    • x的类型VT具有相同的基础类型,并且VT中至少有一个不是命名类型。
    • T是一个接口类型,并且x实现了T
    • x是一个双向通道值,T是一个通道类型,x的类型VT具有相同的元素类型,并且VT中至少有一个不是命名类型。
    • x是预声明的标识符nil,而T是指针、函数、切片、映射、通道或接口类型。
    • x是一个可以用类型T表示的无类型常量。

因此,这解释了为什么testInterface(instance.fails)失败:因为instance.fails不能赋值给f func() interface{}

现在是第二个问题:

如果我尝试从声明为返回interface{}的函数(包装函数)返回*struct,它完全可以工作。

这可以正常工作,因为*struct类型的值可以赋值给interface{}类型,原因如下:

  1. 可赋值性规则中的这条规则:“T是一个接口类型,并且x实现了T。”
  2. “一个类型可以实现包含其方法子集的任何接口,因此可以实现多个不同的接口。例如,所有类型都实现空接口:”(粗体为我添加)

参考资料:

英文:

That's because it does not fit the assignability criterias

> A value x is assignable to a variable of type T ("x is assignable to T") in any of these cases:
>
> * x's type is identical to T.
> * x's type V and T have identical underlying types and at least one of V or T is not a named type.
> * T is an interface type and x implements T.
> * x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not a named type.
> * x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type.
> * x is an untyped constant representable by a value of type T.

So, this explains why testInterface(instance.fails) fails: because the instance.fails is not assignable to the f func() interface{}.

Now the second question:

> It perfectly works if I try to return *struct from function that is declared to return interface{} (wrapper func).

It works fine, because the value of the *struct type is assignable to the interface{} type because of:

  1. this rule of the assignability: "T is an interface type and x implements T."
  2. "A type implements any interface comprising any subset of its methods and may therefore implement several distinct interfaces. For instance, all types implement the empty interface:" (bold is mine)

References:

huangapple
  • 本文由 发表于 2017年4月18日 16:00:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/43466757.html
匿名

发表评论

匿名网友

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

确定