goには2つの値の型があります
- 値型
- 参照型
値型と参照型
関数に渡されるときにコピーされる。
そのため、呼び出し元で引数に指定した値型の変数は関数に渡されるタイミングでコピーされ、コピーされた値が関数の呼び出しに使用されるので、呼び出し元の値は変化しない。
以下のコードで確認する
func main() { a := 1 b := 1 sampleFunc(a, &b) fmt.Println(a) // 値渡し fmt.Println(b) //参照渡し } func sampleFunc(a int, b *int) { a = a + 1 *b = *b + 1 }
メソッド内で使われている a は 呼び出し元のmain関数内の a のコピーであるので、sampleFunc内部で a の値に変更を加えてもコピー先の値が変更されるだけで、main関数内のコピー元のaの値は変更されない。
これを呼び出し先でコピーを渡さずに参照(ポインタ)にするとコピーではなくポインタが渡されて呼び出し先で変更した値は呼び出し元の値にも影響を与えることができるようになる。(サンプルコードでは値b)
なお、値型はメモリ空間の別領域に新しく変数のコピーを作成するので、使いすぎるとメモリ効率が悪くなる。
一方で参照型は変数のアドレスを参照するので、メモリ空間上は同じアドレスを指している。そのため、メモリ効率がいいが、意図せず変数が変更されることがある。(変数の汚染)
参照型でのみ値を持つmap
そもそも、このエントリーを書こうと思ったのは「プログラミング言語Go」の冒頭の方でmapが 参照型
であると明示されていたことによる。
値型
と 参照型
が変数にはそれぞれあることは理解していたが、mapは最初から参照型でのみ値を持つということを理解していなかったので、ここに備忘録として記載。
sampleコード
func main() { a := map[string]string{"apple": "りんご"} sampleFunc(a) fmt.Printf("%v\n", a) } func sampleFunc(a map[string]string){ a["orange"] = "みかん" } // map[orange:みかん apple:りんご] と出力される
sampleFuncに渡している値は参照型でなく通常の値型としてのmap。もし map が値型なら sampleFunc内には map のコピーが渡されて main 内の map は書き変わらないはず。
しかし書き換わっているので、map はデフォルトで参照型のみで値を持つということがわかる。
この他にも参照型のみで値を持つ値に
- slice
- channel
- interface
がある。
参照型を作り出すmake
goで参照型を作成するには make メソッドを使う
// sliceの作成 make([]int, 10) //mapの作成 make(map[string]string)
値型と参照型。ちょっと迷うことが多いけど一度調べてみてよかった。
一通りスターティング的な内容の書籍は読み終わったので、ガッツリ「プログラミング言語Go」を現在呼んでいる。