※注意: この記事ははあくまで個人学習用に整理しただけの記事で、内容としては不完全なものになります。読んでも参考にならない可能性が高いです。
構造体のメソッドを作成するにあたり、値レシーバとポインタレシーバの二つの方法があった。
この二つの違いがわかっていなかった為、まとめてみる。
A Tour of Go に良いサンプルがあったので、こちらで確認。
ポインタレシーバから確認。
ポインタレシーバ
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4}
v.Scale(10)
fmt.Println(v.Abs()) // 50
}
50 と表示される。
上記の Scale メソッドがポインタレシーバになっている。
この Scale のレシーバの * を消して、値レシーバとして実行してみる。
値レシーバ
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func (v Vertex) Scale(f float64) { // * を削除して実行
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4}
v.Scale(10)
fmt.Println(v.Abs()) // 5
}
v.Scale(10)
で10倍にしているはずが、反映されず 5 になってしまった。
A Tour of Go の解説では以下のように書いてある。
変数レシーバでは、 Scale メソッドの操作は元の Vertex 変数のコピーを操作します。 (これは関数の引数としての振るまいと同じです)。 つまり main 関数で宣言した Vertex 変数を変更するためには、Scale メソッドはポインタレシーバにする必要があるのです。
なるほど。
変数を変更したい場合にポインタレシーバを使うんですね。
そして上記の例の Abs メソッドのように変数の変更をしない場合は値レシーバを使用し、戻り値で処理結果を戻し、呼び出し元でその値を使用すると。
なるほどです。一旦ざっくり理解した。