Pointers X未实现Y(…方法具有指针接收器)

Pointers X未实现Y(…方法具有指针接收器),pointers,go,methods,interface,Pointers,Go,Methods,Interface,关于“X不实现Y(…方法有一个指针接收器)”这件事,已经有几个Q&a了,但对我来说,他们似乎在谈论不同的事情,而不适用于我的具体情况 所以,我不是把问题说得很具体,而是把它说得宽泛抽象——似乎有几种不同的情况会导致这个错误发生,有人能总结一下吗 也就是说,如何避免问题,如果问题发生,可能性是什么?Thx.当您尝试将具体类型分配或传递(或转换)到接口类型时,会出现此编译时错误;而类型本身并没有实现接口,只是一个指向该类型的指针 简短摘要:如果分配的值实现了分配给它的接口,则该值有效。如果它是接口的

关于“X不实现Y(…方法有一个指针接收器)”这件事,已经有几个Q&a了,但对我来说,他们似乎在谈论不同的事情,而不适用于我的具体情况

所以,我不是把问题说得很具体,而是把它说得宽泛抽象——似乎有几种不同的情况会导致这个错误发生,有人能总结一下吗


也就是说,如何避免问题,如果问题发生,可能性是什么?Thx.

当您尝试将具体类型分配或传递(或转换)到接口类型时,会出现此编译时错误;而类型本身并没有实现接口,只是一个指向该类型的指针

简短摘要:如果分配的值实现了分配给它的接口,则该值有效。如果它是接口的超集,它将实现它。指针类型的方法集包括同时具有指针和非指针接收器的方法。非指针类型的方法集仅包括具有非指针接收器的方法

让我们看一个例子:

type Stringer interface {
    String() string
}

type MyType struct {
    value string
}

func (m *MyType) String() string { return m.value }
Stringer
接口类型只有一个方法:
String()
。存储在接口值
Stringer
中的任何值都必须具有此方法。我们还创建了一个
MyType
,并使用指针接收器创建了一个方法
MyType.String()
。这意味着
String()
方法位于
*MyType
类型的中,但不在
MyType
的中

当我们试图将
MyType
的值分配给
Stringer
类型的变量时,我们得到了有问题的错误:

m := MyType{value: "something"}

var s Stringer
s = m // cannot use m (type MyType) as type Stringer in assignment:
      //   MyType does not implement Stringer (String method has pointer receiver)
但是,如果我们尝试将
*MyType
类型的值分配给
Stringer
,则一切正常:

s = &m
fmt.Println(s)
我们得到了预期的结果(试一试):

因此获取此编译时错误的要求:

  • 正在分配(或传递或转换)的非指针具体类型的值
  • 分配给(或传递给或转换为)的接口类型
  • 具体类型具有接口所需的方法,但带有指针接收器
解决问题的可能性:

  • 必须使用指向该值的指针,其方法集将包括带有指针接收器的方法
  • 或者接收器类型必须更改为非指针,因此非指针具体类型的方法集也将包含该方法(从而满足接口)。这可能可行,也可能不可行,因为如果方法必须修改值,则非指针接收器不是选项
结构与嵌入 使用时,通常不是“您”实现接口(提供方法实现),而是嵌入到
结构中的类型。如本例所示:

type MyType2 struct {
    MyType
}

m := MyType{value: "something"}
m2 := MyType2{MyType: m}

var s Stringer
s = m2 // Compile-time error again
同样,编译时错误,因为
MyType2
的方法集不包含嵌入的
MyType
String()
方法,只有
*MyType2
的方法集,所以以下方法可以工作(在上尝试):

如果我们嵌入
*MyType
,并且只使用非指针
MyType2
,我们也可以让它工作(在上尝试):

此外,无论我们嵌入什么(无论是
MyType
还是
*MyType
),如果我们使用指针
*MyType2
,它都会一直工作(在以下位置尝试):

规范中的相关章节(章节中):

给定一个结构类型
S
和一个名为
T
的类型,提升的方法包括在结构的方法集中,如下所示:

  • 如果
    S
    包含一个匿名字段
    T
    ,则
    S
    *S
    的方法集都包括使用receiver
    T
    的提升方法。
    *S
    的方法集还包括使用receiver
    *T
    的升级方法
  • 如果
    S
    包含一个匿名字段
    *T
    ,则
    S
    *S
    的方法集都包括使用接收方
    T
    *T
    的提升方法
换句话说,如果我们嵌入了一个非指针类型,那么非指针嵌入器的方法集只能获取具有非指针接收器的方法(来自嵌入类型)

如果我们嵌入一个指针类型,非指针嵌入器的方法集将获得同时具有指针和非指针接收器(来自嵌入类型)的方法

如果我们使用一个指向嵌入器的指针值,无论嵌入类型是否为指针,指向嵌入器的指针的方法集总是获取带有指针和非指针接收器(来自嵌入类型)的方法

注意:

有一种非常类似的情况,即当您有一个接口值,它封装了一个值
MyType
,并且您尝试从中获取另一个接口值
Stringer
。在这种情况下,由于上述原因,断言将不成立,但我们会得到一个稍微不同的运行时错误:

m := MyType{value: "something"}

var i interface{} = m
fmt.Println(i.(Stringer))
运行时死机(在服务器上尝试):

尝试转换而不是类型断言时,会出现我们正在讨论的编译时错误:

m := MyType{value: "something"}

fmt.Println(Stringer(m))

另一种情况是,如果我想创建一个接口,其中一些方法将修改内部值,而另一些方法则不会

type GetterSetter interface {
   GetVal() int
   SetVal(x int) int
}
然后实现此接口的内容可能如下所示:

type MyTypeA struct {
   a int
}

func (m MyTypeA) GetVal() int {
   return a
}

func (m *MyTypeA) SetVal(newVal int) int {
    int oldVal = m.a
    m.a = newVal
    return oldVal
}
因此,实现类型可能会有一些方法是指针接收器,而有些则不是,因为我有很多不同的方法是gettersetter,所以我想在测试中检查它们是否都达到了预期的效果

如果我要做这样的事情:

myTypeInstance := MyType{ 7 }
... maybe some code doing other stuff ...
var f interface{} = myTypeInstance
_, ok := f.(GetterSetter)
if !ok {
    t.Fail()
}
然后我不会得到前面提到的“X没有实现Y(Z方法有指针接收器)”错误(因为这是一个编译时错误),但是我会有一个糟糕的一天去追查为什么我的测试是f
panic: interface conversion: main.MyType is not main.Stringer:
    missing method String
m := MyType{value: "something"}

fmt.Println(Stringer(m))
type GetterSetter interface {
   GetVal() int
   SetVal(x int) int
}
type MyTypeA struct {
   a int
}

func (m MyTypeA) GetVal() int {
   return a
}

func (m *MyTypeA) SetVal(newVal int) int {
    int oldVal = m.a
    m.a = newVal
    return oldVal
}
myTypeInstance := MyType{ 7 }
... maybe some code doing other stuff ...
var f interface{} = myTypeInstance
_, ok := f.(GetterSetter)
if !ok {
    t.Fail()
}
var f interface{} = new(&MyTypeA)
 ...
myTypeInstance := MyType{ 7 }
var f interface{} = &myTypeInstance
...
func SomeStuff(g GetterSetter, x int) int {
    if x > 10 {
        return g.GetVal() + 1
    }
    return g.GetVal()
}
func (m MyTypeA) OtherThing(x int) {
    SomeStuff(m, x)
}
func (m *MyTypeA) OtherThing(x int) {
    SomeStuff(m, x)
}

func (m MyTypeA) OtherThing(x int) {
    SomeStuff(&m, x)
}
package main

import "fmt"

// Loader defines a content loader
type Loader interface {
    Load(src string) string
}

// WebLoader is a web content loader
type WebLoader struct{}

// Load loads the content of a page
func (w *WebLoader) Load(src string) string {
    return fmt.Sprintf("I loaded this page %s", src)
}

func main() {
    webLoader := WebLoader{}
    loadContent(webLoader)
}

func loadContent(loader Loader) {
    loader.Load("google.com")
}
webLoader := &WebLoader{}