😀

Go - メソッドとレシーバ

2024/03/09に公開

目的

メソッドについて理解する
値レシーバとポインタレシーバについても理解する

メソッドとは

レシーバを持つ関数のこと
下のコードでは (s Square)がレシーバ
Square型の構造体sのレシーバを使って、メソッドを定義している

レシーバを作る
// 四角形の構造体とそれの面積を求めるメソッド

type Square struct {
  width  float64
  height float64
}

func (s Square) Area() float64 {
  return s.width * s.height
}

#メソッドを使う
メソッドはレシーバで登録した構造体の変数から呼ぶことができる
下の例ではSquare型の変数が呼んでいる

メソッドを使う
package main

import (
  "fmt"
)

type Square struct {
  width  float64
  height float64
}

func (s Square) Area() float64 {
  return s.width * s.height
}

func main() {
  square := Square{3.0, 4.0}
  fmt.Println(square.Area())
  // output: 12
}

ポインタレシーバとは

今まで見てきたコード内で登場したレシーバは値レシーバという
ポインタレシーバはその名の通りレシーバの引数にポインタを持つ

##二つの違い
ポインタレシーバ:ポインタとして引数で渡すので、関数内でオブジェクトの値を変更できる
値レシーバ:元の値とは別のコピーした値が関数に渡されるので元の値は変更はできない

以下のコードでは四角形の形を長方形の性質を保ったまま変形させるメソッドを作っている
下の例のmain関数では、Square型の変数をポインタに変えて、その後ポインタレシーバのメソッドを呼び出している

ポインタレシーバを使う
package main

import (
  "fmt"
)

type Square struct {
  width  float64
  height float64
}

func (s *Square) Reshape(w float64, h float64) {
  s.width = w
  s.height = h
}

func main() {
  square := Square{3.0, 4.0}
  fmt.Println(square)
  // output: {3 4}

  (&square).Reshape(5.0, 6.0)
  fmt.Println(square)
  // output: {5 6}
}

#補足
上のソースコードでは、ポインタレシーバで定義されたメソッドを使うために、Square型の変数をわざわざポインタに変換していた
実はそんなことしなくても、square.Reshape(5.0, 6.0)でよい

#いつポインタレシーバを使うか
もう一度二つの違いを確認しておく
ポインタレシーバ:ポインタとして引数で渡すので、関数内でオブジェクトの値を変更できる
値レシーバ:元の値とは別のコピーした値が関数に渡されるので元の値は変更はできない

つまりその構造体型のデータの値を変えたい時にポインタレシーバを使う

あともう一点は
レシーバに渡す構造体がメモリ上で大きい領域を占めている場合にポインタレシーバを使う
値レシーバでコピーを渡す場合、その大きなものをメモリ上の別の領域にコピーしてから、そのコピーしたものを渡す必要がある
一方ポインタレシーバではコピーなどせず、そのデータのメモリ上でのアドレスを渡すだけでいいのでメモリにやさしい

#参考文献
A Tour of Go

Discussion