はじめに
本記事は以下のツイートの翻訳転載です。
値レシーバ、またはポインタレシーバのどちらを選択すべきか、という内容になります。
// 値レシーバ
func (c Cat) meow() { ... }
// ポインタレシーバ
func (c *Cat) meow() { ... }
個人的にはこの辺よくわかっていないので、とりあえずポインタレシーバを使ってきました。
が、このTipを読んでもあんまり腹落ちした感じではないです。多分実際に「ああここは値レシーバ/ポインタレシーバを使っておくべきだった」という体験が無いからかなと思います。
Golang Tip #21: ポインタレシーバの推奨
以下リンクはポインタレシーバと値レシーバについての簡単な説明です。
Goでは、「変更を伴う場合はポインタレシーバを、そうでない場合は値レシーバを使用する」というルールは白黒はっきりしない。
ポインタ・レシーバーを選択するのはどのような場合か?
- レシーバの状態を変更する場合
- レシーバが大きい構造体の場合
(上記で書いているように、「大きい」は主観的な場合があります) - レシーバとなる構造体に
sync.Mutex
のような同期フィールドが含まれている場合
(ポインタレシーバを使用することでロックのコピーを避けることができます)
- 確信が持てない場合は、ポインタレシーバに寄せておく方が賢明かもしれません
値レシーバーが適しているのはどのような場合か
- レシーバとなる型が小さく、また変更されない場合
-
map
、func
、channel
、またはサイズやキャパシティが変更されないスライスの場合(スライスの要素は変更されても良い)
なぜサイズやキャパシティが変更されないスライスなのか
値レシーバを通してスライスの要素等1を変更することはできますが、メソッド内でスライスのサイズやキャパシティの変更を行なってもメソッド外へ影響しないからです。
以下は例です。
最後に、一貫性が重要です。
一貫性を保つために、ある構造体に対して値レシーバ/ポインタレシーバを混在させないようにします。
一般的に、ある構造体について一部ポインタレシーバを使用する必要がある場合は、全てのメソッドのレシーバをポインタレシーバにするほうが良いです(ほとんどが値レシーバで十分であるとしても)。
これについて私が思いつく理由は以下です。
- 両方を使用すると、その構造体のインスタンスがどのように操作されるかに混乱や矛盾が生じるため
- インターフェースとオブジェクトの関係の一貫性とわかりやすさを保つため
(参照: https://x.com/func25/status/1757760004768354346?s=20)
-
the elements of the slice or the content of the underlying array
↩