【Golang】構造体のレシーバがポインタ型であるべき理由とは

なぜ構造体のメソッドのレシーバはポインタ型にするの?

ずばり結論は「常に同じ構造体を参照するため」です。

例えば以下のようなコードがあるとします。

package main

import "fmt"

type User struct {
	Name string
}

func (u *User) ChangeName(newName string) {
	u.Name = newName
}

func main() {
	user := User{Name: "田中"}
	fmt.Println(user.Name)  //田中

  user.ChangeName("高橋")
  fmt.Println(user.Name)  //高橋
}

(軽く説明するとUser型の構造体を定義し、Nameフィールドを持てるようにします。ChangeNameメソッドでは引数に渡した名前を構造体のNameに置き換えます。)

上記の例だと、Nameが「田中」のユーザー(User型)を定義し、ChangeNameメソッドを呼び出すことでそのユーザーの名前を「田中」から「高橋」に変更しました。

見事、上のコードだと問題なくNameの変更ができていますね。

では次に、レシーバがポインタ型でないとどうなってしまうのかを確認していきましょう。

レシーバがポインタ型でない場合

package main

import "fmt"

type User struct {
	Name string
}

func (u User) ChangeName(newName string) {
	u.Name = newName
}

func main() {
	user := User{Name: "田中"}
	fmt.Println(user.Name)  //田中

  user.ChangeName("高橋")
  fmt.Println(user.Name)  //田中...あれ?高橋に変えたはずなのに。
}

ChangeNameメソッドのレシーバがポインタ型ではなく値型となってます。(その点以外変更はないので説明は割愛します)

上記のコードだとChangeNameメソッドで引数に「高橋」を渡しているのに、userのNameは「田中」のままです。

なぜこのようなことが起きるかというと、レシーバがポインタ型でないからです。

どういうことかと言うと、レシーバが値型だとメソッド内で使われるレシーバ(u)はuserそのものではなく、userのコピーが対象となっているからです。

これに対してポインタ型の場合、レシーバはアドレス番号を確認しuserそのものを対象とします。

そのため、レシーバが値型の時はChangeNameメソッドを使ってもuserのNameが変わらず、userのコピーが作られてそのNameが変わっただけとなった訳です。

タイトルとURLをコピーしました