Go言語

【Go言語】メソッドのレシーバーとnil

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判定をしている。

 

 

イチオシ記事

1

自己紹介 フリーランスエンジニアをしているヨノと申します。 独学でプログラミングを学び、ソシャゲ・SaaS開発などを経て、2018年からフリーランスエンジニアとして活動しています。 主にバックエンド中 ...

2

はじめまして、フリーランスエンジニアのヨノと申します。 自己紹介 独学でプログラミングを学び、ソシャゲ・SaaS開発などを経て、2018年からフリーランスエンジニアとして活動しています。 主にバックエ ...

3

ネット上で色々言われているフリーランスエンジニア....。「本当はどうなの?」と思っている人は多いでしょう。 そこで本記事ではフリーランスエンジニア5年生の私が、ネット上の意見も引用しながら実態を解説 ...

-Go言語