Go言語マスター 〜メソッド・レシーバ〜

公開日:2022-10-23
go

https://youtu.be/5lhK4SVVGMc

はじめに

Goのメソッドやレシーバについて解説します。

Methods

Goにはクラスの代わりに、型にメソッドを定義できます。

以下の例はX, Yの2つのメンバーを持ち、Absというメソッドが定義されています。メソッドは名前の前に(v Vertex)というようにレシーバ引数を記述します。
初期化した後に、v.Absというようにしてメソッドを呼び出すことができます。

type Vertex struct {
  X, Y float64
}

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

func main() {
  v := Vertex{3, 4}
  fmt.Println(v.Abs())
}

Methods continued

上述のstructだけでなく、任意の型にメソッドを宣言できます。ただし、型が同じパッケージにある必要があります。

例えば、float64にメソッドを定義したい場合、float64は同パッケージ内で定義された型ではないのでtype MyFloat float64というように定義しておく必要があります。

type MyFloat float64

func (f MyFloat) Abs() float64 {
  if f < 0 {
    return float64(-f)
  }
  return float64(f)
}

Pointer receivers

ポインタレシーバでメソッドを宣言できます。

ポインタレシーバーを用いることで、元の値に変更を加えるようなメソッドを作成できます。ポインタを使わない変数レシーバーの場合には、元の値のコピーがレシーバーとして渡されます。

func (v *Vertex) Scale(f float64) {
  v.X = v.X * f
  v.Y = v.Y * f
}

Methods and pointer indirection

ポインタレシーバーScaleと、ポインタを引数に取るScaleFunc関数を比べてみましょう。

func (v *Vertex) Scale(f float64) {
  v.X = v.X * f
  v.Y = v.Y * f
}

func ScaleFunc(v *Vertex, f float64) {
  v.X = v.X * f
  v.Y = v.Y * f
}

この時、ポインタを引数に取るため、ポインタではない値を渡すとコンパイルエラーとなります。

var v Vertex
ScaleFunc(v, 5)  // Compile error!
ScaleFunc(&v, 5) // OK

一方メソッドは、ポインタレシーバーである場合には自動的にポインタ渡しとして解釈してくれます。

var v Vertex
v.Scale(5)  // OK
p := &v
p.Scale(10) // OK

この性質は逆も同じで、値を受け取る関数にポインタは渡せませんが、変数レシーバーにポインタを指定しても自動的に値渡しとして解釈されます。

Choosing a value or pointer receiver

値レシーバとポインタレシーバの使い分けですが、基本的にはポインタレシーバで統一すると良いとされます。

メソッドを呼び出す度にコピーが行われてメモリを消費することを避けたり、この後学習するインターフェースの利用のためにも便利です。

おわりに

今回は、Goでのメソッドやレシーバについて解説しました。やや高度な機能ですが、良い構成でコードを書くには欠かせない機能です。活用できるようになりましょう。