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

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

在这个X上已经有几个Q&A没有实现Y。。。方法有一个指针接收器,但对我来说,它们似乎在谈论不同的事情,而不适用于我的具体情况

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


也就是说,如何避免问题,如果问题发生,可能性是什么?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,则一切正常:

我们得到了预期的结果,试着在:

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

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

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

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的方法集都包括接收方T的升级方法。方法集*S还包括接收方*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,所以我想在测试中检查它们是否都是d 正在按预期进行

如果我要做这样的事情:

myTypeInstance := MyType{ 7 }
... maybe some code doing other stuff ...
var f interface{} = myTypeInstance
_, ok := f.(GetterSetter)
if !ok {
    t.Fail()
}
那么我就不会得到前面提到的X不实现Y Z方法有指针接收器错误,因为这是一个编译时错误,但我会有一天不好的时间去追查我的测试失败的确切原因

相反,我必须确保使用指针进行类型检查,例如:

var f interface{} = new(&MyTypeA)
 ...
或:

然后大家都对测试感到满意

但是等等!在我的代码中,可能有一些方法在某处接受GetterSetter:

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)
}

简而言之,假设您有这段代码,并且有一个加载程序接口和一个实现此接口的WebLoader

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")
}
所以这段代码会给你这个编译时错误

./main.go:20:13:无法将webLoader类型webLoader用作类型加载器 在loadContent的参数中: WebLoader未实现Loader加载方法具有指针接收器

因此,您只需要将webLoader:=webLoader{}更改为以下内容:

webLoader := &WebLoader{} 

既然定义了这个函数func w*WebLoader Load来接受指针接收器,那么为什么它会被修复呢。欲了解更多解释,请阅读@icza和@karora answers

从以上答案延伸感谢您的所有答案 我认为显示指针/非指针结构的所有方法会更自然

这是操场代码。

总结所有的例子

指针结构类型将包括所有非指针/指针接收器方法 非指针结构类型将仅包括非指针接收器方法。 对于嵌入式结构

非指针外部结构+非指针嵌入结构=>仅非指针接收器方法。 非指针外部结构+指针嵌入结构/指针外部结构+非指针嵌入结构/指针外部结构+指针嵌入结构=>所有嵌入方法
谢谢你非常全面的回答。很抱歉,因为奇怪,我没有收到SO通知,所以回复晚了。在我搜索的一个案例中,答案是成员函数要么是所有指针类型,例如func m*MyType,要么是none。是这样吗?我是否可以混合使用不同类型的成员函数,例如func m*MyType和func m MyType?@xpt您可以混合使用指针接收器和非指针接收器,但不需要完全相同。如果有19个方法使用指针接收器,而有一个方法使用非指针接收器,这就很奇怪了。如果你开始混合它们,那么跟踪哪些方法是哪些类型的方法集的一部分也会变得更加困难。这个答案中的更多细节:如果不能将MyType更改为使用值方法,那么如何实际解决注释结尾提到的问题:使用{}包装MyType值的接口。我试过这样的东西&I.*Stringer,但它不起作用。甚至有可能吗?@JoelEdström是的,有可能,但毫无意义。例如,您可以键入assert非指针类型的值并将其存储在变量中,例如x:=i.MyType,然后您可以调用带有指针接收器的方法,例如i.String,它是&i.String的缩写,因为变量是可寻址的,所以成功。但是改变值的指针方法指向的值不会反映在接口值中包装的值中,这就是为什么它没有什么意义。@DeepNightS的方法集中不包括*T的两个方法,因为s可能不可寻址,例如函数返回值或映射索引的结果,另外,由于通常只存在/接收副本,并且如果允许获取其地址,则带有指针接收器的方法只能修改副本混淆,就像您假设原始副本已修改一样。请参阅此答案以获取示例:。到目前为止,这是最容易理解的注释。并直接解决了我面临的问题。@Maxs728同意这一点,在回答众多围棋问题时非常罕见。切中要害且易于理解。截止日期中间我需要什么
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{}