【Golang】ポインタがよくわからなかったので調べたものをまとめてみた

おはこんばんにちは!
最近一気に秋が深まって冬まで行きそうな勢いで寒いですね。。
ハロウィーンでまちが盛り上がってる中、自分はGolangの習得に苦戦してます。
(・◞◟・`;;)オゥ…
元々Railsをやっていた自分としてはやはりポインタとか構造体の扱い方は結構詰まるところになってしまっているので、今回色々調べていたことをメモ程度にまとめようかなと思っています。
そこ違うとかあればぜひご指摘ください。

変数ってどうなってるの?

色々本とかを読み漁っているとこんな記述を見つけました。

ポインタの値は変数のアドレスです。つまり、ポインタとは値が格納されている場所なのです。
全ての変数はアドレスを持っています。
引用元:プログラミング言語Go(丸善出版)

つまり、もともと変数には値とポインタというプロパティ?が存在しているということか。。?(本当にあってるのかな?w) f:id:bi-hideaki:20171101215105p:plain

&演算子→アドレスを生成する

*演算子→ポインタが参照している変数を取り出す

値渡し

さて、Go言語では引数は全て値渡しで渡されます。そもそも値渡しとは?

値渡し

渡された変数の値が別のアドレスにコピーされる。コピーされるために、関数内で変更しても呼び出し元の値は変化しない。
ふむ、つまりGo言語では基本的に変数を定義して、それを引数に渡して関数で処理をしても呼び出し元の値を変更することは基本的に出来ないらしい。となると手続き型で関数で呼び出すことが基本となるGo言語ではかなり融通が効かなくなる?ってことか。

呼び出し元の値を変更することを実現するポインタ

Go言語では基本的に値渡しになるということで、呼び出し元の変数の値を変更するためにポインタ型と言うものが使われるらしい。
&演算子で変数のアドレスを生成して、それを引数として渡すことで呼び出し元の変数の値を操作することが出来るということか。
ちなみに、引数として渡すアドレスも当然値渡しなのでコピーされることになる。
コードを例に見てみると何をやってるかわかりやすい。

num := 100
pointer := &num //変数numのアドレスを生成してそれをpointerに格納する
fmt.println(*pointer) //pointerにはnumのアドレスが入っているので、*演算子を使ってnumの値にアクセスする
=> 100
*pointer += 1 // num += 1 と同義
fmt.println(*pointer) // "101"

結局、ポインタってのは「値渡しで呼び出し元の変数の値を操作するために使われるもの」という認識に今はなったが果たしてそれであっているのだろうか??

ポインタと構造体

Go言語で開発をしているとたくさん使うのが構造体とポインタ型のコンビネーションだ。
構造体はフィールドと呼ばれる複数の型の値をまとめて持っておけるものだ。
関数で呼び出して構造体の値を更新してDBにインサートするのはよくある流れだ。そこで構造体の値を変更したい。ただ単に引数に渡すと値渡しなのでコピーされてしまってうまくインサートできなくなってしまう。(同じ関数内で構造体の値を変えてインサートまでするコードを書けば行けるのかもだがそれはナンセンスですよね。。)
そこでポインタ型を使って呼び出し元の構造体のフィールドの値を変更して、それをDBにインサートする処理を書けばうまく行きそうですね。おそらくポインタはこういったことを実現するためにあるのかな。
今の自分の乏しい知識だとそれくらいしか思いつかないです。。

ポインタレシーバを持つメソッド

Go言語を始めてからポインタと同じくはじめは違和感を感じまくっていたのが関数とメソッドの使い分けです。これに関しては別の記事を書こうかなと思っています。
Go言語では関数の呼び出しで個々の引数の値を値渡し(コピー)するので関数が変数の値を更新する必要がある場合にはポインタを使って変数のアドレスを渡して呼び出し元の変数の値を操作することが出来る。
これはレシーバ変数を更新する必要があるメソッドでも同様のことがいえるってことですね。

type Num struct { X, Y int }

func (n *Num) IncBy(ele int) {
  n.X += ele
  n.Y += ele
}

とりあえずポインタを渡せばよいはだめ!?

今後色々とメソッドを作ったりしていくと思うのですが、とりあえずポインタを渡せば良いというのはだめなようです。
qiita.com
この記事にもあるようにGo言語では値型の型と参照型の型があるのでそれらによってポインタを渡すかどうかをしっかりと使い分けないとだめなようです。(パニックして落ちるわけではなさそう)

  • 値型:数値、構造体、配列
  • 参照型:文字列、インターフェース、マップ、スライス、チャネル

参照型というものに分類されるものは型の仕組みにポインタを使った参照を含んでいるらしくポインタを生成しなくてもいいようです。

ここまでポインタに関してまとめてきました。まだまだわかってないことが多すぎて辛い。ということで引き続き勉強がんばります、、