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

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

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

最佳答案

当您尝试将具体类型分配或传递(或转换)为接口(interface)类型时,会出现此编译时错误;而类型本身并没有实现接口(interface),只是一个指向类型的指针。
简短摘要: 接口(interface)类型变量的 assignment 是有效的,如果被分配的值实现了它分配给的接口(interface)。如果它的 method set 是接口(interface)的超集,它就会实现它。指针类型的方法集包括具有指针和非指针接收器的方法。非指针类型的方法集仅包括具有非指针接收器的方法。
让我们看一个例子:

type Stringer interface {
    String() string
}

type MyType struct {
    value string
}

func (m *MyType) String() string { return m.value }
Stringer 接口(interface)类型只有一种方法: String() 。存储在接口(interface)值 Stringer 中的任何值都必须具有此方法。我们还创建了一个 MyType ,我们创建了一个方法 MyType.String() 指针 接收器。这意味着 String() 方法在 *MyType 类型的 method set 中,但不在 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)
我们得到了预期的结果(在 Go Playground 上尝试):
something
所以要求得到这个编译时错误:
  • 被分配(或传递或转换)的非指针具体类型的值
  • 分配给(或传递给或转换为)的接口(interface)类型
  • 具体类型有接口(interface)需要的方法,但是有一个指针接收器

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

  • 结构和嵌入
    使用 structs and embedding 时,通常不是“您”实现接口(interface)(提供方法实现),而是您嵌入到 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 的方法集,只有 0x2518122431341 的方法集在 14231341 之后才起作用(1412313123)
    var s Stringer
    s = &m2
    
    我们也可以让它工作,如果我们嵌入 *MyType2 并且只使用一个非指针 *MyType (在 Go Playground 上试试):
    type MyType2 struct {
        *MyType
    }
    
    m := MyType{value: "something"}
    m2 := MyType2{MyType: &m}
    
    var s Stringer
    s = m2
    
    此外,无论我们嵌入什么(MyType2MyType ),如果我们使用指针 *MyType ,它将始终有效(在 0x2518122133 上尝试)
    type MyType2 struct {
        *MyType
    }
    
    m := MyType{value: "something"}
    m2 := MyType2{MyType: &m}
    
    var s Stringer
    s = &m2
    
    规范中的相关部分(来自部分 Go Playground ):

    所以换句话说:如果我们嵌入一个非指针类型,那么非指针嵌入器的方法集只会获取带有非指针接收器的方法(来自嵌入类型)。
    如果我们嵌入一个指针类型,则非指针嵌入器的方法集获取具有指针和非指针接收器的方法(来自嵌入类型)。
    如果我们使用指向嵌入器的指针值,无论嵌入类型是否为指针,指向嵌入器的指针的方法集总是同时获取具有指针和非指针接收器的方法(来自嵌入类型)。
    注:
    有一个非常相似的情况,即当您有一个包含 *MyType2 值的接口(interface)值,并且您尝试从中获取另一个接口(interface)值 MyType Go Playground 时。在这种情况下,由于上述原因,断言将不成立,但我们会得到一个略有不同的运行时错误:
    m := MyType{value: "something"}
    
    var i interface{} = m
    fmt.Println(i.(Stringer))
    
    运行时 panic (在 Struct types 上尝试):
    panic: interface conversion: main.MyType is not main.Stringer:
        missing method String
    
    尝试转换而不是类型断言,我们得到了我们正在谈论的编译时错误:
    m := MyType{value: "something"}
    
    fmt.Println(Stringer(m))
    

    关于pointers - X 没有实现 Y (...方法有一个指针接收器),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40823315/

    10-15 00:57
    查看更多