[Go] ポインタを取り扱う記号の紛らわしさ

ポインタを取り扱うコードって難しいですよね。プログラミングを習得する上で一つの鬼門になっているようです。

なぜポインタを取り扱うコードは難しいのか、それは文法の記号の紛らわしさが一因になっていると思います。

ポインタを取り扱うコードを書く時に *& どちらを書けばいいんだっけ?と迷ったことはありませんか?私はいつも迷います。

なぜ *& のどちらかを書くかで迷うのか

Go のコード例を示しつつ、理由を考察してみます。

以下の text は string 型の変数で、textRef は string のポインタ型の変数を表します。

var text string
var textRef *string

これらの変数に代入をしてみましょう。

// まずは普通に代入
text = "コーヒー"

// & を付けることで text を指し示すポインタとして代入する
textRef = &text

次にこれらの変数を使う時のことを考えてみます。
fmt.Println() で各変数の値を出力してみます。

// 出力: コーヒー
fmt.Println(text)

// 出力: 0xc000010230 (変数 text を指し示すアドレス)
fmt.Println(textRef)

// 出力: コーヒー (* を付けることで上のアドレスが指し示す先の値を得られる)
fmt.Printf(*textRef)

Go のポインタの基本文法は以上です。
ポインタを取り扱うための記号は基本的に &* の二つだけです。

二つの記号の意味を整理します。

* (アスタリスク) & (アンパサンド)
型を記述する時 型名の前に * を付けることでポインタ型を表す
変数を参照する時 変数名の前に * を付けることでポインタの先の値を表す 変数名の前に & を付けることでその変数のポインタを表す

同じ記号でも使う場面によって意味が異なる点に気がつくと思います。

string をポインタ型にしたいと思ったときに型名の先頭に * につける一方で、
ポインタ変数を string にしたいと思ったときも変数名の先頭に * を付けるのです。

感覚的な話になりますが * は使う場面によって正反対の意味を持つ印象を受けると思います。

私は未だにこれで取り違えることがありますが、なぜ紛らわしいのか認識できているだけでも楽になると思ったので言語化してみました。

理解しやすい記号の覚え方があるといいなと思っていますが、未だに答えが出ていません。(gopls などの Language Server を使ってコーディングしていれば、間違った時に怒られてすぐ気がつけるので大きな支障はありませんが…。)