kg-icon
Go言語100Tips - 6章
2025-10-11

値とポインタを扱うレシーバ型について

Goに限らずほとんどのプログラミング言語は関数を扱うことができます。

例えばこれは関数です。

go
1func f(name string) string {
2	return "hello" + name
3}

nameという入力を受け取り、出力を生成し、その出力を返します。

また、メソッドというものがあります。メソッドは型に関連付けられた関数のことを言います。

例えばこんな感じ。

go
1type User struct {
2	name string
3}
4
5func (u *User) updateName(name string) {
6	u.name = name
7}

updateNameというメソッドはUserという構造体に関連付けられています。

関数を特定の型に関連付けることで、その型にメソッドを生やすことができます。これをポインタレシーバと言います。

ここで、自身が疑問に思ったことなのですが、コード例に出てきた*User*とは何なのでしょう。

これはポインタを表しており、*をつけることでポインタとしてのUserを定義することができます。

User 型の値そのもの」ではなく、「User 型の値への参照(アドレス)」を扱うことができるようになるわけです。

そのため、(u *User) と書くことで、メソッド内から User のフィールドを直接変更できるようになります。

つまり以下のコードだとフィールドの値を更新することはできません。

go
1type User struct {
2	name string
3}
4
5func (u User) updateName(name string) {
6	u.name = name
7}

*がないと値レシーバとなります。

値レシーバは値のコピーが作成され、変更はメソッド内でのみ有効なんですね。なので元のオブジェクトのフィールドは更新されないわけです。

厳密に言えばポインタレシーバもコピーは行われていますがポインタのコピーが行われています。

Goを始めたばかりでも、テストコードを書きながら動作確認していればおかしいことに気がつきますが、注意が必要なところですね。

名前付き結果パラメータについて

名前付きパラメータとは以下のようなコードです。

go
1func f(name string) (newName string) {
2	newName = name + " is new"
3	return
4}
5
6func main() {
7	fmt.Println(f("Hoge"))
8}

返り値に名前をつけ、それと同じ変数名を定義すると、明示的にreturnに書かなくてもそれが返るようになります。

ただし注意点があって、関数やメソッド開始時に結果パラメータは初期化されます。

つまり以下のコードはコンパイルできません。

go
1func f(name string) (newName string) {
2	newName := name + " is new"
3	return
4}
5
6func main() {
7	fmt.Println(f("Hoge"))
8}

newNameは最初の時点で初期化されている、つまり空文字列として定義されているので、:=で初期定義できません。=の上書きなら問題ないです。

こんな感じで、初期値の扱いにも注意する必要があるので、使い所は慎重に決めたほうがいい気がしました。

また、関数自体がすごく長いと出力を覚えておきながらコードを読む必要があるので認知負荷が高くなります。短い関数であればその負荷も少なく済みます。