Go言語のメソッドには
- 値レシーバ(変数レシーバ)
- ポインタレシーバ
の2つがある。
値レシーバ
値レシーバは、メソッド呼び出し時に変数の値をコピーし、新たな値をメソッドに渡す。
そのためメソッド内で変数を変更してももとの値は変更されない。
次のように、「名」を更新するメソッドを値レシーバで定義しても呼び出し元の値は更新されない。
type User struct { FirstName string LastName string } func (u User) FullName() string { return u.LastName + " " + u.FirstName } func (u User) UpdateFirstName(firstName string) { u.FirstName = firstName } func main() { user := User{ FirstName: "太郎", LastName: "田中", } fmt.Println(user.FullName()) // 田中 太郎 user.UpdateFirstName("次郎") fmt.Println(user.FullName()) // 田中 太郎 }
ポインタレシーバ
ポインタレシーバの場合、もとの値のポインタを渡す(値のコピーはしない)ためメソッド内で変数の値を変更すると、もとの値も変更される。
type User struct { FirstName string LastName string } func (u *User) FullName() string { if u == nil { return "" } return u.LastName + " " + u.FirstName } func (u *User) UpdateFirstName(firstName string) { u.FirstName = firstName } func main() { user := User{ FirstName: "太郎", LastName: "田中", } // コンパイラが暗黙的に、(&user).FullName()としてくれている fmt.Println(user.FullName()) // 田中 太郎 user.UpdateFirstName("次郎") fmt.Println(user.FullName()) // 田中 次郎 }
nilもレシーバになり得る
nil
に対してメソッドを呼び出そうとするとエラーになるかと思いきや、必ずしもそうではありません。
マップやスライス、ポインタなど、nil
がその型で意味を持つゼロ値の場合はnil
もレシーバになり得る。
マップの例
マップのゼロ値はnil
なので、レシーバにnil
が来ても許される。
例えば次のようなメソッド呼び出しをしてもエラーにならない。
type ValuesMap map[string]int func (m ValuesMap) Keys() []string { keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) } return keys } func main() { var m ValuesMap fmt.Println(m.Keys()) // [] }
ポインタレシーバの場合
変数 T
のポインタは *T
型で、ゼロ値は nil
。
なので、ポインタレシーバの場合もレシーバにnil
がきても許される。
type User struct { FirstName string LastName string } func (u *User) FullName() string { if u == nil { return "" } return u.LastName + " " + u.FirstName } func (u *User) UpdateFirstName(firstName string) { u.FirstName = firstName } func main() { u1 := User{ FirstName: "太郎", LastName: "田中", } fmt.Println(u1.FullName()) // 田中 太郎 var u2 User fmt.Println(u2.FullName()) // "" }
※ レシーバがnil
でもメソッド呼び出しはできるが、フィールド呼び出しをしたときにエラーになるので、nil
判定をしている。