X 没有实现 Y(...方法有一个指针接收器)

新手上路,请多包涵

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

所以,我没有让问题变得非常具体,而是让它变得宽泛和抽象——似乎有几种不同的情况会导致这个错误发生,有人可以总结一下吗?

即,如何避免问题,如果发生,有哪些可能性?谢谢。

原文由 xpt 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 879
2 个回答

当您尝试将 具体 类型分配或传递(或转换)为接口类型时,会出现此编译时错误;并且类型本身不实现接口,只是 指向类型 的指针。

简短摘要: 如果 分配 的值实现了分配给它的接口,则对接口类型变量的分配是有效的。如果它的 方法集 是接口的超集,它就会实现它。指针类型的方法集包括 具有 指针和非指针接收器的方法。非指针类型的方法集 包括非指针接收者的方法。

让我们看一个例子:

 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 的方法集中。

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

 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)

但是,如果我们尝试将 --- 类型的值分配给 Stringer *MyType ,一切都很好:

 s = &m
fmt.Println(s)

我们得到了预期的结果(在 Go Playground 上尝试):

 something

所以得到这个编译时错误的要求:

  • 分配(或传递或转换)的 非指针 具体类型的值
  • 分配给(或传递给或转换为)的接口类型
  • 具体类型具有接口所需的方法,但带有 指针接收器

解决问题的可能性:

  • 必须使用指向该值的指针,其方法集将包括具有指针接收器的方法
  • 或者必须将接收者类型更改 _为非指针_,因此非指针具体类型的方法集也将包含该方法(从而满足接口)。这可能可行也可能不可行,就好像该方法必须修改值一样,非指针接收器不是一种选择。

结构和嵌入

使用 结构和嵌入 时,实现接口(提供方法实现)的通常不是“您”,而是您嵌入到 struct 中的类型。就像在这个例子中:

 type MyType2 struct {
    MyType
}

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

var s Stringer
s = m2 // Compile-time error again

再次,编译时错误,因为 MyType2 的方法集不包含 String() 嵌入的方法 MyType 方法集 *MyType2--- ,因此以下工作(在 Go Playground 上尝试):

 var s Stringer
s = &m2

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

 type MyType2 struct {
    *MyType
}

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

var s Stringer
s = m2

此外,无论我们嵌入什么( MyType*MyType ),如果我们使用指针 *MyType2 尝试,它总是可以工作(地面):

 type MyType2 struct {
    *MyType
}

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

var s Stringer
s = &m2

规范中的相关部分(来自 Struct types 部分):

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

  • If S contains an anonymous field T , the method sets of S and *S both include promoted methods with receiver T*S 的方法集还包括带有接收器 *T 的提升方法。
  • If S contains an anonymous field *T , the method sets of S and *S both include promoted methods with receiver T*T

所以换句话说:如果我们嵌入了一个非指针类型,那么非指针嵌入器的方法集只会得到带有非指针接收者的方法(来自嵌入类型)。

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

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

笔记:

有一个非常相似的情况,即当您有一个包含值 MyType 的接口值时,您尝试从中 键入断言 另一个接口值 Stringer 。在这种情况下,由于上述原因,断言将不成立,但我们会得到一个稍微不同的运行时错误:

 m := MyType{value: "something"}

var i interface{} = m
fmt.Println(i.(Stringer))

运行时恐慌(在 Go Playground 上尝试):

 panic: interface conversion: main.MyType is not main.Stringer:
    missing method String

尝试转换而不是类型断言,我们得到了我们正在谈论的编译时错误:

 m := MyType{value: "something"}

fmt.Println(Stringer(m))

原文由 icza 发布,翻译遵循 CC BY-SA 4.0 许可协议

为了保持简短,假设您有一个 Loader 接口和一个实现该接口的 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: 不能在 loadContent 的参数中使用 webLoader(WebLoader 类型)作为 Loader 类型:WebLoader 没有实现 Loader(Load 方法有指针接收器)

要修复它,您只需将 webLoader := WebLoader{} 更改为以下内容:

 webLoader := &WebLoader{}

为什么这会解决这个问题?因为你定义了这个函数 func (w *WebLoader) Load 来接受一个指针接收器。如需更多解释,请阅读@icza 和@karora 的回答

原文由 Saman 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题