为什么我不能将类型值分配给实现具有该类型的接收器类型指针的方法的接口?

时间:2016-07-03 05:07:39

标签: go

我在Golang的世界里已经2天了,经历了 go tour 。我无法帮助,但却注意到一种我似乎无法通过适当推理得出的特殊性。

此代码运行正常:

package main
import (
    "fmt"
    "math"
)
type Vertex struct{
    X,Y float64
}
type Abser interface{
    Abs() float64
}
func (v Vertex) Abs() float64{ //method with value receiver argument
    return math.Sqrt(v.X*v.X+v.Y*v.Y)
}
func main(){
    var myVer Vertex = Vertex{3,4}
    var inter Abser
    inter = &myVer //assigning *Vertex type to inter
    fmt.Println(inter.Abs())
}

同时,以下代码显示错误:

package main
import (
    "fmt"
    "math"
)
type Vertex struct{
    X,Y float64
}
type Abser interface{
    Abs() float64
}
func (v *Vertex) Abs() float64{ //method with pointer receiver argument
    return math.Sqrt(v.X*v.X+v.Y*v.Y)
}
func main(){
    var myVer Vertex = Vertex{3,4}
    var inter Abser
    inter = myVer //assigning Vertex type to inter
    fmt.Println(inter.Abs())
}

错误是:

  

interface.go:18:不能在赋值时使用myVer(类型为Vertex)作为Abser类型:       Vertex没有实现Abser(Abs方法有指针接收器)

在到达巡演的这一部分之前,我可以理解Go的创作者已经放弃了像

这样繁琐的符号。
  

(* v)的.method1name()

     

(&安培; v)的.method2name()

因此具有值接收器的方法可以与值和指针一起使用,反之亦然。

为什么语言在使用接口时会区分两者(值和指针)?如果相同的参考/解引用原则可以适用于此更方便吗?

我希望我不会遗漏一些太明显的东西。 谢谢!

4 个答案:

答案 0 :(得分:4)

Intro++ to Go Interfaces”说明了问题:

  

*Vertex是一种类型。它是“指向Vertex”类型的指针。它是(非指针)Vertex的不同类型。关于它作为指针的部分是其类型的一部分。

您需要类型的一致性。

Methods, Interfaces and Embedded Types in Go”:

  

确定接口合规性的规则基于这些方法的接收器以及如何进行接口调用   Here are the rules in the spec for how the compiler determines if the value or pointer for our type implements the interface

  • 相应指针类型*T的方法集是接收方*TT
  • 的所有方法的集合
  

这个规则说明如果我们用来调用特定接口方法的接口变量包含一个指针,那么基于值和指针的接收器的方法将满足接口。

  • 任何其他类型T的方法集包含接收方类型为T的所有方法。
  

这条规则说明,如果我们用来调用特定接口方法的接口变量包含一个值,那么只有基于值的接收器的方法才能满足接口。

关于 Karrot Kake

answer method set详见go wiki

  

接口类型的方法集是它的接口。

     

存储在界面中的具体值不可寻址,与map元素无法寻址的方式相同。
  因此,当您在接口上调用方法时,它必须具有相同的接收器类型,或者必须直接从具体类型中辨别出来。

     

指针和值接收器方法可以分别使用指针和值进行调用,如您所料   可以使用指针值调用值接收器方法,因为它们可以首先取消引用   无法使用值调用指针接收器方法,因为存储在接口内的值没有地址

(“没有地址”实际上意味着它是not addressable

答案 1 :(得分:3)

指针类型method set包含值接收方法,但为值设置的方法不包括指针接收方法。为值类型设置的方法不包括指针接收器方法,因为存储在接口中的值不是addressable

答案 2 :(得分:2)

好问题。
这是Golang类型系统: Vertex*Vertex是不同的类型

看到这个澄清样本:
您可以使用type Vertex2 Vertex定义新的Vertex2类型,这些类型也是不同的类型:

虽然这有效:

package main

import "fmt"
import "math"

func main() {
    var myVer Vertex = Vertex{3, 4}
    var inter Abser = myVer //assigning Vertex type to inter
    fmt.Println(inter.Abs())
}

func (v Vertex) Abs() float64 { //method with pointer receiver argument
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

type Vertex struct {
    X, Y float64
}
type Abser interface {
    Abs() float64
}

这不起作用:

package main

import "fmt"
import "math"

func main() {
    var myVer Vertex = Vertex{3, 4}
    var inter Abser = myVer //assigning Vertex type to inter
    fmt.Println(inter.Abs())
}

type Vertex2 Vertex

func (v Vertex2) Abs() float64 { //method with pointer receiver argument
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

type Vertex struct {
    X, Y float64
}
type Abser interface {
    Abs() float64
}

错误是:

。\ m.go:8:在分配时不能使用myVer(类型Vertex)作为Abser类型:
     Vertex没有实现Abser(缺少Abs方法)

与您的样本完全相同的错误:

package main

import "fmt"
import "math"

func main() {
    var myVer Vertex = Vertex{3, 4}
    var inter Abser = myVer //assigning Vertex type to inter
    fmt.Println(inter.Abs())
}

func (v *Vertex) Abs() float64 { //method with pointer receiver argument
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

type Vertex struct {
    X, Y float64
}
type Abser interface {
    Abs() float64
}

因为两个Vertex和Vertex2是不同的类型 并且这也有效

package main

import "fmt"
import "math"

func main() {
    var inter Abser = &Vertex{3, 4} //assigning &Vertex type to inter
    fmt.Printf("inter: %#[1]v\n", inter)
    fmt.Println(inter.Abs())
}

func (v Vertex) Abs() float64 { //method with pointer receiver argument
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

type Vertex struct {
    X, Y float64
}
type Abser interface {
    Abs() float64
}

输出:

inter: &main.Vertex{X:3, Y:4}
5

,因为:
类型可以具有与其关联的方法集。接口类型的方法集是其接口。 任何其他类型T的方法集由使用接收器类型T声明的所有方法组成。相应指针类型* T的方法集是使用receiver * T或T声明的所有方法的集合(也就是说,它也是包含T)的方法集。进一步的规则适用于包含匿名字段的结构,如结构类型一节中所述。任何其他类型都有一个空方法集。在方法集中,每个方法必须具有唯一的非空方法名称。

类型的方法集确定类型实现的接口以及可以使用该类型的接收器调用的方法。
参考:https://golang.org/ref/spec#Method_sets

答案 3 :(得分:0)

存储在界面中的值无法寻址 - 但为什么?有关答案,请参阅here

tl; dr 因为当接口中随后存储了不同类型B的值时,指向接口中A类值的A的指针将失效。