Tour of Go のエクササイズ 51 で遊んでいました。説明では、 へのポインターの代わりに をScale
受け取った場合、メソッドは効果がないと主張しています。Vertex
Vertex
しかし、宣言v := &Vertex{3, 4}
をv := Vertex{3, 4}
に変更すると、出力の唯一の変更は、ポインターをマークするためmain
の欠落です。&
Scale
では、変数がポインターでなくても、受け取った変数を変更するのはなぜでしょうか?
Tour of Go のエクササイズ 51 で遊んでいました。説明では、 へのポインターの代わりに をScale
受け取った場合、メソッドは効果がないと主張しています。Vertex
Vertex
しかし、宣言v := &Vertex{3, 4}
をv := Vertex{3, 4}
に変更すると、出力の唯一の変更は、ポインターをマークするためmain
の欠落です。&
Scale
では、変数がポインターでなくても、受け取った変数を変更するのはなぜでしょうか?
値を「受け取る」わけではありません。Go は厳密に型付けされているため、T へのポインターがどこかに規定さ*T
れている場合、そのような型付けされた場所の値として発生する可能性のある唯一のオプションは T ( ) へのポインターです。
「魔法」はコンパイラーにあり、特定の条件下でコードを効果的に「書き換え」ます。
のメソッド セット (の型)に が含まれ、引数リストを のパラメータ リストに割り当てることができる場合、メソッド呼び出し
x.m()
は有効です。がアドレス指定可能で、&x のメソッド セットに が含まれている場合、は の省略形です。x
m
m
x
m
x.m()
(&x).m()
関連:メソッドセット
ツアーで示唆されている違いは、実際には に変更v := &Vertex{3, 4}
しv:= Vertex{3, 4}
たことではなく、2 つのメソッドの定義を変更して、ポインターではなく値を操作できるようにしたことです。したがって、たとえば、 forは次のようScale
にfunc (v *Vertex) Scale(f float64) {...
なります( はポインタ値であり、 は非ポインタ値になることにfunc (v Vertex) Scale(f float64) {...
注意してください)。どちらの場合も、asの宣言を残す必要があります。(v *Vertex)
(v Vertex)
v
v := &Vertex{3, 4}
最初のケースでは、メソッドがポインターを受け取る場合、出力は&{15 20} 25
. ただし、メソッドがポインターではなく値を取る場合、出力は&{3 4} 5
.
どちらの場合も、オブジェクトv
へのポインターです。Vertex
最初のケースでは、ポインターがメソッドに渡され、すべてが期待どおりに機能しVertex
ます。オブジェクトに加えられた変更は元の値に対して行われるため、これらの変更はメソッドが戻った後も保持されます。2 番目のケースでは、v
はまだポインタですが、Go コンパイラは に変換するのに十分スマートなのでv.Scale(5)
、(*v).Scale(5)
はv
逆参照され、結果の値は に渡されScale
ます。