为什么接口赋值比方法调用更严格?

Why is an interface assignment more strict than a method call?

我正在经历围棋之旅。我了解到,如果我们有一个接受指针作为接收者的方法,它也会接受一个值类型作为接收者(go 会自动进行转换)。

type Vertex struct { X, Y float64 }

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

那么下面的代码是有效的,Vertex是按值接收还是按指针接收

v := Vertex{1, 2}
fmt.Println(v.Abs())

p := &v
fmt.Println(p.Abs())

但是,假设我们有以下接口:

type Abser interface {
    Abs() float64
}

那么,为什么下面的代码无效呢?

var a Abser
v := Vertex{1, 2}

a = v // invalid

我的理解是这样就好了。即使 v 是一个“实现”接受指针接收器的 Abs 函数的值类型,它也会按值接受它?

接口是否只是简单地设计为在接口变量可以保存在右侧的内容方面更加严格?该接口将 *Vertex 和 Vertex 视为两种不同的类型,但是,Abs() 方法也没有处理问题。

在这两种情况下,Go 都需要一个指向 运行 方法的指针。最大的区别是方法调用会自动获取 v 的地址,但检查是否实现了接口则不会。

方法调用:

在普通类型上调用带有指针接收器的方法时,如果允许,Go 将自动获取地址。来自 spec on method calls(突出显示是我的):

A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m()

在这种情况下,x 指的是您的变量 v,即 addressable。所以在方法调用上,Go 自动 运行s (&v).Abs().

作业:

当试图assign a = v, the check that must be fullfilled is T is an interface type and x implements T.. v implements Abser only if its method set匹配接口时。本方法组确定如下:

The method set of any other type T consists of all methods declared with receiver type T. The method set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T (that is, it also contains the method set of T).

你会注意到,在计算方法集时,Go 并没有像在方法调用中那样获取 v 的地址。 说明var v Vertex设置的方法为空,接口实现失败

解决方案:

解决这个问题的方法是自己获取 v 的地址:

var a Abser
v := Vertex{1, 2}

a = &v

通过这样做,您现在正在查看 *Vertex 的方法集,其中确实包含 Abs() float64 并因此实现了接口 Abser.