インターフェイスの割り当てがメソッド呼び出しよりも厳密なのはなぜですか?

Aug 20 2020

Tour ofGoを行っています。ポインターをレシーバーとして受け入れるメソッドがある場合、値型もレシーバーとして受け入れることを学びました(自動的に変換を実行します)。

type Vertex struct { X, Y float64 }

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

次に、頂点が値またはポインタのどちらで受信されるかに関係なく、次のコードが有効です。

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を2つの異なるタイプとして認識していますが、Abs()メソッドにも問題の処理はありません。

回答

3 Marc Aug 20 2020 at 12:32

どちらの場合も、Goはメソッドを実行するためのポインターを必要としています。大きな違いは、メソッド呼び出しは自動的にのアドレスを取得しますがv、何かがインターフェイスを実装しているかどうかを確認することはしません。

メソッド呼び出し:

プレーンタイプでポインターレシーバーを使用してメソッドを呼び出す場合、許可されている場合、Goはアドレスを自動的に取得します。メソッド呼び出しの仕様から(ハイライトは私のものです):

xのメソッドセット(のタイプ)にmが含まれ、引数リストをmのパラメーターリストに割り当てることができる場合、メソッド呼び出しxm()は有効です。xがアドレス可能で、&xのメソッドセットにmが含まれている場合、xm()は(&x).m()の省略形です。

この場合、xは変数vを参照し、アドレス指定可能です。したがって、メソッド呼び出しでは、Goが自動的に実行され(&v).Abs()ます。

割り当て:

を割り当てよ うとする場合a = v、満たす必要のあるチェックはT is an interface type and x implements T.です。メソッドセットがインターフェイスと一致する場合にのみv実装されます。このメソッドセットは次のように決定されます。Abser

他のタイプTのメソッドセットは、レシーバータイプTで宣言されたすべてのメソッドで構成されます。対応するポインター型* Tのメソッド・セットは、レシーバー* TまたはTで宣言されたすべてのメソッドのセットです(つまり、Tのメソッド・セットも含まれています)。

メソッドセットを計算するとき、Goはvメソッド呼び出しで行ったようにのアドレスを取得しないことに気付くでしょう。これは、に設定されたメソッドvar v Vertexが空であり、インターフェイスの実装に失敗したことを意味します。

解決:

これを回避する方法は、v自分のアドレスを取得することです。

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

a = &v

これを行うことにより、インターフェイスを*Vertex含むメソッドセットを確認していることにAbs() float64なりますAbser