【ITニュース解説】Go (Golang) Basic - Pointers
2025年09月28日に「Dev.to」が公開したITニュース「Go (Golang) Basic - Pointers」について初心者にもわかりやすく解説しています。
ITニュース概要
Go言語のポインタは、データの記憶場所を示す重要な概念だ。デフォルトの「値渡し」ではデータのコピーが渡され、元の値は変わらない。しかし、ポインタを使うと元のデータに直接アクセスし、変更が可能になる。特にメソッドのレシーバにポインタを使うことで、元の構造体を確実に操作でき、パフォーマンス向上にも繋がる。
ITニュース解説
Go言語において、ポインタという概念は初めて学ぶ人にとって難しく感じられるかもしれないが、効果的なGoコードを書く上で不可欠な要素である。これは、データを関数やメソッドに渡す際に、データの「コピー」を渡すのか、それとも「元のデータへのアクセス」を渡すのかという根本的な違いを理解することから始まる。
Go言語では、デフォルトでデータを関数に渡す場合、そのデータのコピーが作成される。この仕組みを「値渡し」と呼ぶ。例えば、Userという構造体があり、これにNameとEmailというフィールドがあると想像してみる。main関数内でUser型の変数userを作成し、そのEmailアドレスを更新するupdateEmailというメソッドを呼び出す場合を考える。このupdateEmailメソッドがレシーバとしてUser構造体のコピーを受け取るように定義されていると、メソッド内部でEmailをnewEmailに変更しても、それはUserのコピーに対する変更となる。結果として、updateEmailメソッドの呼び出しが終わった後で元のuser変数のEmailを確認しても、何も変更されていないことがわかる。これは、メソッドが元のデータではなく、その複製である「値」を操作していたためである。具体的には、メソッドの内部でu.Email = newEmailと変更しても、uはuserのコピーであるため、user自体のEmailフィールドは初期値のままであるという挙動になる。
この問題、つまり元のデータを変更したい場合には、ポインタを用いる必要がある。ポインタは、データそのものを保持するのではなく、データがコンピュータのメモリ上のどこに格納されているか、その「メモリ上のアドレス」を保持する特殊な変数である。関数やメソッドがポインタを受け取ると、そのアドレスを通じて元のデータに直接アクセスし、変更を加えることができるようになる。Go言語では、ポインタを扱うために二つの特別な記号を使用する。一つはアスタリスク(*)で、これは型宣言の際に「User型のデータへのポインタ」という意味で使用する。もう一つはアンパサンド(&)で、これは変数の前に付けて「この変数のメモリ上のアドレスを取得する」という意味で使用する。
ポインタが最も頻繁に使われ、その力を発揮するのがメソッドのレシーバ部分である。メソッドの定義において、レシーバの型にアスタリスク(*)を追加することで、そのメソッドが構造体のコピーではなく、元の構造体インスタンス、つまり元のデータに直接アクセスして操作するようにGoコンパイラに指示できる。先ほどのupdateEmailメソッドの例を再び取り上げる。このメソッドのレシーバをu Userからu *Userに変更すると、updateEmailメソッドはUser構造体へのポインタを受け取るようになる。こうすると、メソッド内部でのu.Email = newEmailという操作は、ポインタが指し示す元のUser構造体のEmailフィールドを直接変更するようになる。main関数からこのメソッドを呼び出す際、user.updateEmail("budi.baru@email.com")のように記述しても、Goは自動的にuser変数のアドレスをメソッドに渡してくれるため、特別な記述は必要ない。この修正により、メソッド呼び出し後にはuser変数のEmailが正しく更新されていることが確認できる。
ポインタレシーバを使用することには主に二つの重要な理由がある。一つは、先述のようにメソッドが元のデータを変更できるようにするためである。これが、メソッドが構造体の状態を変更できる唯一の方法となる。もう一つの理由はパフォーマンスに関わる。特に大きな構造体を扱う場合、メソッドが呼び出されるたびにその構造体のコピーを作成することは、時間とメモリを消費する操作となる。ポインタを渡せば、データ全体をコピーする代わりに、メモリ上のアドレスという小さな情報だけを渡すことになるため、プログラムの実行速度を向上させ、メモリの使用効率を高めることができる。
結論として、ポインタは関数やメソッドが元のデータに直接アクセスし、変更を加えたい場合に不可欠な概念である。システムエンジニアを目指す初心者にとっての指針として、構造体に対してメソッドを記述する際には、常にポインタレシーバ(例: func (s *MyStruct) ...)を使用することがGo言語の標準的な慣習であり、これにより多くの一般的なバグやパフォーマンスの問題を未然に防ぐことができる。この原則を理解し適用することは、Go言語での堅牢で効率的なコード記述の第一歩となる。