Goの参照渡しについて調べてみた
Goでは全てが値渡し

Goにおける参照渡し=ポインタの値渡し
Goでは関数にパラメータを渡すとき、全て値渡しで実現されています。
(C派生の言語はすべてそうらしいです)
じゃあ、参照渡しって何?ってなりますよね。
参照渡し=ポインタの値渡しです。
つまり、ポインタそのものを渡しているわけではなく、ポインタのコピーを渡しています。
値渡しと参照渡しの差は、内部の値をコピーするかどうかです。
こちらについては後ほど例を交えて説明します。
今回の内容はGo公式ドキュメントの『Pointers and Allocation』
の章に
詳細な記載があります。
本記事では、『Pointers and Allocation』 から要点を抜粋して紹介します。
値渡しと参照渡しの違いは内部値のコピー有無
まずは、先述した
値渡しと参照渡しの差は、内部の値をコピーするかどうかです。
について詳しく見ていきます。
公式ドキュメント『When are function parameters passed by value?』 の節に以下の記述があります。
For instance, passing an int value to a function makes a copy of the int, and passing a pointer value makes a copy of the pointer, but not the data it points to.
たとえば、int値を関数に渡すとintのコピーが作成され、ポインター値を渡すとポインターのコピーが作成されますが、ポインターが指すデータは作成されません。
つまり、
- 値渡し:値のコピーが作成される
- 参照渡し:ポインタのコピーは作成されるが、ポインタが指すデータ(値)のコピーは作成しない
といった差があります。
図にすると以下のとおりです。
同じ色の箱はアドレスが同じだと考えてください。
(図が下手なところはほっといてあげてください🙇♂️)

大きな差がありますね。
この差により、例えば、多くのフィールドを持つ構造体を関数の引数やレシーバとして渡す場合、
値渡しでは全フィールドのコピーが行われてしまうため
パフォーマンス的に良くないといった違いが生まれてきます。
無駄なコピーを行わないために全て参照渡しにしとけばいいか、
というとそれはまた別で考慮すべき点が出てきます。
「値渡し または 参照渡しのどちらを使用するか」については多くの議論がなされています。
- Go公式ドキュメント『Should I define methods on values or pointers?』
- Yury Pitsishin『Pass by pointer vs pass by value in Go』
- pospomeのプログラミング日記『golang の 引数、戻り値、レシーバをポインタにすべきか、値にすべきかの判断基準について迷っている』
- THE Finatext Tech Blog『Go言語(golang)における値渡しとポインタ渡しのパフォーマンス影響について』
コードで確認
では、最後にここまでの内容をコードで確認して終わります。
上記のplaygroundを実行すると、以下のように出力されました。
(アドレス部分は実行ごとに異なります)
main()における構造体のアドレス: 0xc00010a040
main()におけるnameのアドレス: 0xc00010a040
PassByReference()における構造体のアドレス: 0xc000102020
PassByReference()におけるnameのアドレス: 0xc00010a040
PassByValue()における構造体のアドレス: 0xc00010a050
PassByValue()におけるnameのアドレス: 0xc00010a050
PassByReference()がレシーバを参照渡しで受け取る関数でPassByValue()がレシーバを値渡しで受け取る関数です。
main()とPassByReference()を比較すると、両者の構造体のアドレスが異なっています。Human構造体がもつnameフィールドについては同じアドレスを指しています。
つまり、レシーバのポインタはコピーしたものを参照していますが、
フィールドの値はコピーではなく、main()で定義したものが使用されています。
先述の内容と一致しますね。
一方で、main()とPassByValue()を比較すると、
両者の構造体のアドレスおよびフィールドのアドレスがすべて異なります。
すなわち、レシーバのポインタおよびフィールドのすべての値に関して、コピーしたものを参照しています。
こちらも先述の内容と一致します。
納得!
ちなみに、なぜすべて値渡しで実現しているかについては、
先程紹介した下記の記事で触れられています。
Yury Pitsishin『Pass by pointer vs pass by value in Go』
→「Passing by value often is cheaper」の章



