即使结构指针未实现接口,为什么我们仍可以将结构指针分配给接口变量? 程序1 程序2

问题描述

我在下面介绍了两个程序:程序1和程序2。

我希望程序1无法编译,并且确实无法编译。这样很好。

我希望程序2无法编译,但可以成功!这个问题是关于程序2成功的原因。

程序1

https://play.golang.org/p/qX9nY8VLlx0

package main

import (
    "fmt"
    "math"
)

type Abser interface {
    Abs() float64
}

type Vertex struct {
    X float64
    Y float64
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    var a Abser
    
    a = Vertex{3,4}
    fmt.Println(a.Abs())
}

无法通过此错误进行编译:

./prog.go:24:4: cannot use Vertex literal (type Vertex) as type Abser in assignment:
Vertex does not implement Abser (Abs method has pointer receiver)

我之所以会遇到此错误,是因为*Vertex实现了Abser,但Vertex没有实现,因此我们无法将Vertex对象分配给Abser变量。 / p>

程序2

https://play.golang.org/p/4bIs-fHGhYm

package main

import (
    "fmt"
    "math"
)

type Abser interface {
    Abs() float64
}

type Vertex struct {
    X float64
    Y float64
}

func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    var a Abser
    
    a = &Vertex{3,4}
    fmt.Println(a.Abs())
}

这将成功编译。该程序的输出为:

5

为什么成功?在这里Vertex实现了Abser,但是*Vertex没有实现Abser。然后如何为*Vertex分配类型为Abser的值?

要知道为什么成功,我需要了解哪些语言语义规则?

解决方法

assignability要求阐明了使用接口时的要求:

值x可分配给类型T的变量(“ x可分配给 T“)如果满足以下条件之一:

... T是接口类型,x实现T。 ...

要确定“ x实现T”的规则,我们转向method sets的概念:

类型可能具有与之关联的方法集。一个方法集 接口类型是它的接口。任何其他类型T的方法集 包含以接收方类型T声明的所有方法。

乍看之下,这意味着第一个示例中的Vertex方法集为空,而第二个示例中的*Vertex方法集也为空。

但是,规范会继续指定:

相应指针类型* T的方法集为所有 用接收者* T或T声明的方法(也就是说,它还包含 T的方法集。

这意味着{strong>方法集*Vertex自动包含Vertex的方法集,但反之则不包括。

这在您的第一个示例中无济于事,但是在第二个示例中,Vertex的方法集确实包含Abs() float64。由于这是实现Abser接口所需的全部内容,因此*Vertex被认为是实现Abser的地方。